@usefillo/react 0.9.0 → 0.11.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 +40 -2
- package/dist/index.js +1063 -490
- package/dist/styles.css +249 -30
- package/dist/styles.unlayered.css +249 -30
- package/package.json +3 -2
package/dist/index.js
CHANGED
|
@@ -1,19 +1,31 @@
|
|
|
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 useRef8,
|
|
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,
|
|
18
|
+
isField as isField2,
|
|
9
19
|
isFilloError as isFilloError2,
|
|
20
|
+
isLikelyDevEnv,
|
|
10
21
|
needsExplicitSubmit,
|
|
11
22
|
normalizeFormSchema,
|
|
12
23
|
normalizeFormTheme,
|
|
13
24
|
reachableFields,
|
|
14
|
-
reachablePageSequence
|
|
25
|
+
reachablePageSequence,
|
|
26
|
+
resolveThemeAppearance
|
|
15
27
|
} from "@usefillo/core";
|
|
16
|
-
import { resolveStrings, slotClass as slotClass3 } from "@usefillo/core";
|
|
28
|
+
import { REQUIRED_FIELD_MESSAGE as REQUIRED_FIELD_MESSAGE2, resolveStrings, slotClass as slotClass3 } from "@usefillo/core";
|
|
17
29
|
|
|
18
30
|
// src/appearance.tsx
|
|
19
31
|
import { createContext, useContext } from "react";
|
|
@@ -61,11 +73,96 @@ function useStrings() {
|
|
|
61
73
|
return useContext(FilloStringsContext);
|
|
62
74
|
}
|
|
63
75
|
|
|
76
|
+
// src/chrome.tsx
|
|
77
|
+
import { isBuildTimeDevEnv } from "@usefillo/core";
|
|
78
|
+
import { Fragment, jsx, jsxs } from "react/jsx-runtime";
|
|
79
|
+
function safeHttpUrl(href) {
|
|
80
|
+
try {
|
|
81
|
+
if (!href) return null;
|
|
82
|
+
const url = new URL(href);
|
|
83
|
+
if (url.protocol !== "http:" && url.protocol !== "https:") return null;
|
|
84
|
+
return url.toString();
|
|
85
|
+
} catch {
|
|
86
|
+
return null;
|
|
87
|
+
}
|
|
88
|
+
}
|
|
89
|
+
var warnedPreviewInProduction = false;
|
|
90
|
+
function warnPreviewInProduction() {
|
|
91
|
+
if (warnedPreviewInProduction || isBuildTimeDevEnv()) return;
|
|
92
|
+
warnedPreviewInProduction = true;
|
|
93
|
+
console.warn(
|
|
94
|
+
"[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."
|
|
95
|
+
);
|
|
96
|
+
}
|
|
97
|
+
function PreviewBadge() {
|
|
98
|
+
return /* @__PURE__ */ jsx("span", { className: "fillo-preview-badge", "data-fillo": "preview-badge", children: "Preview" });
|
|
99
|
+
}
|
|
100
|
+
function DevNoClientNotice() {
|
|
101
|
+
return /* @__PURE__ */ jsxs("div", { className: "fillo-devwarning", role: "alert", children: [
|
|
102
|
+
/* @__PURE__ */ jsx("strong", { children: "No Fillo client connected." }),
|
|
103
|
+
" This form is render-only and won\u2019t save responses. Pass a ",
|
|
104
|
+
/* @__PURE__ */ jsx("code", { children: "client" }),
|
|
105
|
+
" to collect them in Fillo, or forward them with webhooks. (This notice only appears in development and preview.)"
|
|
106
|
+
] });
|
|
107
|
+
}
|
|
108
|
+
function DevDraftNotice({ warningUrl }) {
|
|
109
|
+
const safeUrl = safeHttpUrl(warningUrl);
|
|
110
|
+
return /* @__PURE__ */ jsxs("div", { className: "fillo-devwarning", role: "alert", children: [
|
|
111
|
+
/* @__PURE__ */ jsx("strong", { children: "Draft form." }),
|
|
112
|
+
" It renders here, but it won\u2019t accept responses until you publish it in your Fillo dashboard.",
|
|
113
|
+
" ",
|
|
114
|
+
safeUrl && /* @__PURE__ */ jsxs(Fragment, { children: [
|
|
115
|
+
"Connect storage to publish:",
|
|
116
|
+
" ",
|
|
117
|
+
/* @__PURE__ */ jsx("a", { href: safeUrl, target: "_blank", rel: "noopener noreferrer", children: safeUrl }),
|
|
118
|
+
".",
|
|
119
|
+
" "
|
|
120
|
+
] }),
|
|
121
|
+
"(This notice only appears in development and preview.)"
|
|
122
|
+
] });
|
|
123
|
+
}
|
|
124
|
+
function DevStagedNotice() {
|
|
125
|
+
return /* @__PURE__ */ jsxs("div", { className: "fillo-devwarning", role: "alert", children: [
|
|
126
|
+
/* @__PURE__ */ jsx("strong", { children: "Code changes are staged." }),
|
|
127
|
+
" This local preview is not the live form yet. Publish the changes in Fillo before deploying them for respondents."
|
|
128
|
+
] });
|
|
129
|
+
}
|
|
130
|
+
function DevSyncErrorNotice({ error }) {
|
|
131
|
+
return /* @__PURE__ */ jsxs("div", { className: "fillo-devwarning", role: "alert", children: [
|
|
132
|
+
/* @__PURE__ */ jsxs("strong", { children: [
|
|
133
|
+
"Form sync needs attention",
|
|
134
|
+
error.code ? ` (${error.code})` : "",
|
|
135
|
+
"."
|
|
136
|
+
] }),
|
|
137
|
+
" ",
|
|
138
|
+
error.message,
|
|
139
|
+
" The safe form remains available for development."
|
|
140
|
+
] });
|
|
141
|
+
}
|
|
142
|
+
function DevChrome(props) {
|
|
143
|
+
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;
|
|
144
|
+
if (!props.preview && !notice) return null;
|
|
145
|
+
return /* @__PURE__ */ jsxs(Fragment, { children: [
|
|
146
|
+
props.preview && /* @__PURE__ */ jsx(PreviewBadge, {}),
|
|
147
|
+
notice
|
|
148
|
+
] });
|
|
149
|
+
}
|
|
150
|
+
|
|
64
151
|
// src/context.ts
|
|
65
152
|
import { allFields } from "@usefillo/core";
|
|
66
153
|
import { createContext as createContext2, useContext as useContext2, useId, useMemo } from "react";
|
|
67
154
|
var FilloContext = createContext2(null);
|
|
68
155
|
var FilloInstanceIdContext = createContext2(null);
|
|
156
|
+
var FilloChromeContext = createContext2(null);
|
|
157
|
+
function useFilloChrome() {
|
|
158
|
+
return useContext2(FilloChromeContext);
|
|
159
|
+
}
|
|
160
|
+
var noopAnnounce = () => {
|
|
161
|
+
};
|
|
162
|
+
var FilloAnnounceContext = createContext2(noopAnnounce);
|
|
163
|
+
function useFilloAnnounce() {
|
|
164
|
+
return useContext2(FilloAnnounceContext);
|
|
165
|
+
}
|
|
69
166
|
function useFilloInstanceId() {
|
|
70
167
|
const instanceId = useContext2(FilloInstanceIdContext);
|
|
71
168
|
const fallbackId = useId().replace(/:/g, "");
|
|
@@ -128,6 +225,12 @@ function useFilloController(options) {
|
|
|
128
225
|
surface: options.surface ?? "headless",
|
|
129
226
|
// Wrapped so the engine (created once) always calls the latest resolver.
|
|
130
227
|
resolveFormId: options.resolveFormId ? () => optsRef.current.resolveFormId() : void 0,
|
|
228
|
+
// A getter, not a captured boolean: the dev-chrome gate upgrades right
|
|
229
|
+
// after hydration (build-time snapshot → hostname-aware check), but the
|
|
230
|
+
// engine is created once at mount and reads this at submit time.
|
|
231
|
+
get verboseResolutionErrors() {
|
|
232
|
+
return optsRef.current.verboseResolutionErrors;
|
|
233
|
+
},
|
|
131
234
|
respondent: options.respondent,
|
|
132
235
|
// Challenge wiring read through optsRef so the engine (created once) sees
|
|
133
236
|
// the latest token/reset each submit — the widget solves after mount.
|
|
@@ -164,11 +267,11 @@ function useFilloController(options) {
|
|
|
164
267
|
[store, state, options.form, options.formId, options.client]
|
|
165
268
|
);
|
|
166
269
|
}
|
|
167
|
-
function useDraftFlush(api) {
|
|
168
|
-
const
|
|
270
|
+
function useDraftFlush(api, enabled = true) {
|
|
271
|
+
const active = enabled && Boolean(api.form.settings.saveProgress);
|
|
169
272
|
const flush = api.flushDraft;
|
|
170
273
|
useEffect(() => {
|
|
171
|
-
if (!
|
|
274
|
+
if (!active || typeof window === "undefined") return;
|
|
172
275
|
const onPageHide = () => flush();
|
|
173
276
|
const onVisibility = () => {
|
|
174
277
|
if (document.visibilityState === "hidden") flush();
|
|
@@ -179,7 +282,7 @@ function useDraftFlush(api) {
|
|
|
179
282
|
window.removeEventListener("pagehide", onPageHide);
|
|
180
283
|
document.removeEventListener("visibilitychange", onVisibility);
|
|
181
284
|
};
|
|
182
|
-
}, [
|
|
285
|
+
}, [active, flush]);
|
|
183
286
|
}
|
|
184
287
|
|
|
185
288
|
// src/define.ts
|
|
@@ -230,8 +333,15 @@ function useCodeFormSync(form, client, onError, options) {
|
|
|
230
333
|
error: null,
|
|
231
334
|
noticeError: null,
|
|
232
335
|
staged: false,
|
|
336
|
+
accepting: void 0,
|
|
337
|
+
acceptingReason: void 0,
|
|
338
|
+
uploadsAvailable: void 0,
|
|
339
|
+
uploadFileSizeLimitMb: void 0,
|
|
233
340
|
resolvedSchema: void 0,
|
|
234
|
-
resolvedTheme: void 0
|
|
341
|
+
resolvedTheme: void 0,
|
|
342
|
+
warning: void 0,
|
|
343
|
+
warningCode: void 0,
|
|
344
|
+
warningUrl: void 0
|
|
235
345
|
} : {
|
|
236
346
|
key: syncKey,
|
|
237
347
|
client,
|
|
@@ -286,11 +396,22 @@ function useCodeFormSync(form, client, onError, options) {
|
|
|
286
396
|
error: null,
|
|
287
397
|
noticeError,
|
|
288
398
|
staged: Boolean(r.staged),
|
|
399
|
+
accepting: r.accepting,
|
|
400
|
+
acceptingReason: r.acceptingReason,
|
|
401
|
+
uploadsAvailable: r.uploadsAvailable,
|
|
402
|
+
uploadFileSizeLimitMb: r.uploadFileSizeLimitMb,
|
|
289
403
|
resolvedSchema: r.resolvedSchema,
|
|
290
|
-
resolvedTheme: r.resolvedTheme
|
|
404
|
+
resolvedTheme: r.resolvedTheme,
|
|
405
|
+
warning: r.warning,
|
|
406
|
+
warningCode: r.warningCode,
|
|
407
|
+
warningUrl: r.warningUrl
|
|
291
408
|
});
|
|
292
409
|
const dashboardUrl = `${client.baseUrl || "https://fillo.so"}/forms/${r.formId}`;
|
|
293
|
-
if (r.warning)
|
|
410
|
+
if (r.warning) {
|
|
411
|
+
console.warn(
|
|
412
|
+
`[fillo] ${r.warning}${r.warningUrl ? ` Connect storage to publish: ${r.warningUrl}` : ""}`
|
|
413
|
+
);
|
|
414
|
+
}
|
|
294
415
|
if (noticeError) {
|
|
295
416
|
console.warn(
|
|
296
417
|
`[fillo] form sync needs attention (${noticeError.code}): ${noticeError.message}`
|
|
@@ -337,8 +458,15 @@ function useCodeFormSync(form, client, onError, options) {
|
|
|
337
458
|
noticeError: synced.noticeError,
|
|
338
459
|
syncing: Boolean(syncKey) && !synced.settled,
|
|
339
460
|
staged: synced.staged,
|
|
461
|
+
accepting: synced.accepting,
|
|
462
|
+
acceptingReason: synced.acceptingReason,
|
|
463
|
+
uploadsAvailable: synced.uploadsAvailable,
|
|
464
|
+
uploadFileSizeLimitMb: synced.uploadFileSizeLimitMb,
|
|
340
465
|
resolvedSchema: synced.resolvedSchema,
|
|
341
|
-
resolvedTheme: synced.resolvedTheme
|
|
466
|
+
resolvedTheme: synced.resolvedTheme,
|
|
467
|
+
warning: synced.warning,
|
|
468
|
+
warningCode: synced.warningCode,
|
|
469
|
+
warningUrl: synced.warningUrl
|
|
342
470
|
} : {
|
|
343
471
|
formId: null,
|
|
344
472
|
status: null,
|
|
@@ -350,12 +478,25 @@ function useCodeFormSync(form, client, onError, options) {
|
|
|
350
478
|
}
|
|
351
479
|
|
|
352
480
|
// src/fields.tsx
|
|
353
|
-
import {
|
|
354
|
-
|
|
481
|
+
import {
|
|
482
|
+
formatAnswer,
|
|
483
|
+
formatGroupedNumber,
|
|
484
|
+
isField,
|
|
485
|
+
isValidPartialNumberText,
|
|
486
|
+
parseGroupedNumber,
|
|
487
|
+
pipeBlock,
|
|
488
|
+
radioGroupStep,
|
|
489
|
+
REQUIRED_FIELD_MESSAGE,
|
|
490
|
+
shouldAutoSubmit,
|
|
491
|
+
slotClass as slotClass2,
|
|
492
|
+
visibleFields
|
|
493
|
+
} from "@usefillo/core";
|
|
494
|
+
import { memo, useEffect as useEffect6, useLayoutEffect as useLayoutEffect2, useMemo as useMemo4, useRef as useRef6, useState as useState5 } from "react";
|
|
355
495
|
|
|
356
496
|
// src/phone.tsx
|
|
357
497
|
import {
|
|
358
498
|
PHONE_COUNTRIES,
|
|
499
|
+
PHONE_PICKER_COUNTRIES,
|
|
359
500
|
countryByIso,
|
|
360
501
|
countryByTimeZone,
|
|
361
502
|
digitsOnly,
|
|
@@ -366,7 +507,7 @@ import {
|
|
|
366
507
|
toE164
|
|
367
508
|
} from "@usefillo/core";
|
|
368
509
|
import { useEffect as useEffect3, useLayoutEffect, useMemo as useMemo3, useRef as useRef3, useState as useState2 } from "react";
|
|
369
|
-
import { jsx, jsxs } from "react/jsx-runtime";
|
|
510
|
+
import { jsx as jsx2, jsxs as jsxs2 } from "react/jsx-runtime";
|
|
370
511
|
function localeCountry() {
|
|
371
512
|
if (typeof navigator === "undefined") return void 0;
|
|
372
513
|
const tag = navigator.languages?.[0] || navigator.language || "";
|
|
@@ -407,8 +548,9 @@ function PhoneField({ field, value, error, setValue, ids: providedIds }) {
|
|
|
407
548
|
const guess = countryByTimeZone(browserTimeZone()) ?? countryByIso(localeCountry());
|
|
408
549
|
if (guess) setCountry(guess);
|
|
409
550
|
}, []);
|
|
551
|
+
const [pendingRaw, setPendingRaw] = useState2(null);
|
|
410
552
|
const national = nationalFor(stored, country);
|
|
411
|
-
const display = formatNational(country, national);
|
|
553
|
+
const display = pendingRaw ?? formatNational(country, national);
|
|
412
554
|
const inputRef = useRef3(null);
|
|
413
555
|
const caretDigits = useRef3(null);
|
|
414
556
|
useLayoutEffect(() => {
|
|
@@ -435,29 +577,37 @@ function PhoneField({ field, value, error, setValue, ids: providedIds }) {
|
|
|
435
577
|
const caret = el.selectionStart ?? raw.length;
|
|
436
578
|
caretDigits.current = digitsOnly(raw.slice(0, caret)).length;
|
|
437
579
|
if (raw.trimStart().startsWith("+")) {
|
|
438
|
-
const p = parsePhone(raw);
|
|
580
|
+
const p = parsePhone(raw, country, { previousDigits: national });
|
|
581
|
+
if (p.pending) {
|
|
582
|
+
setPendingRaw(p.raw ?? raw);
|
|
583
|
+
setValue(p.raw ?? raw);
|
|
584
|
+
return;
|
|
585
|
+
}
|
|
586
|
+
setPendingRaw(null);
|
|
439
587
|
if (p.country) setCountry(p.country);
|
|
440
588
|
commit(p.country ?? country, p.country ? p.national : digitsOnly(raw));
|
|
441
589
|
return;
|
|
442
590
|
}
|
|
591
|
+
setPendingRaw(null);
|
|
443
592
|
commit(country, digitsOnly(raw));
|
|
444
593
|
}
|
|
445
594
|
function pickCountry(c) {
|
|
446
595
|
touched.current = true;
|
|
596
|
+
setPendingRaw(null);
|
|
447
597
|
setCountry(c);
|
|
448
598
|
commit(c, national);
|
|
449
599
|
inputRef.current?.focus();
|
|
450
600
|
}
|
|
451
601
|
const describedBy = [field.description ? ids.descriptionId : null, error ? ids.errorId : null].filter(Boolean).join(" ") || void 0;
|
|
452
|
-
return /* @__PURE__ */
|
|
453
|
-
/* @__PURE__ */
|
|
602
|
+
return /* @__PURE__ */ jsxs2("div", { ...slots.wrapperProps(`fillo-field fillo-field--phone${error ? " fillo-field--error" : ""}`), children: [
|
|
603
|
+
/* @__PURE__ */ jsxs2("label", { className: slots.label, "data-fillo": "label", id: ids.labelId, htmlFor: ids.inputId, children: [
|
|
454
604
|
field.label,
|
|
455
|
-
!field.required && /* @__PURE__ */
|
|
605
|
+
!field.required && /* @__PURE__ */ jsx2("span", { className: "fillo-optional", children: strings.optional })
|
|
456
606
|
] }),
|
|
457
|
-
field.description && /* @__PURE__ */
|
|
458
|
-
/* @__PURE__ */
|
|
459
|
-
/* @__PURE__ */
|
|
460
|
-
/* @__PURE__ */
|
|
607
|
+
field.description && /* @__PURE__ */ jsx2("p", { className: slots.description, "data-fillo": "fieldDescription", id: ids.descriptionId, children: field.description }),
|
|
608
|
+
/* @__PURE__ */ jsxs2("div", { className: "fillo-phone", children: [
|
|
609
|
+
/* @__PURE__ */ jsx2(CountrySelect, { country, onPick: pickCountry, labelledBy: ids.labelId }),
|
|
610
|
+
/* @__PURE__ */ jsx2(
|
|
461
611
|
"input",
|
|
462
612
|
{
|
|
463
613
|
ref: inputRef,
|
|
@@ -476,7 +626,7 @@ function PhoneField({ field, value, error, setValue, ids: providedIds }) {
|
|
|
476
626
|
}
|
|
477
627
|
)
|
|
478
628
|
] }),
|
|
479
|
-
error && /* @__PURE__ */
|
|
629
|
+
error && /* @__PURE__ */ jsx2("p", { className: slots.error, "data-fillo": "error", id: ids.errorId, children: error })
|
|
480
630
|
] });
|
|
481
631
|
}
|
|
482
632
|
function CountrySelect({
|
|
@@ -484,6 +634,8 @@ function CountrySelect({
|
|
|
484
634
|
onPick,
|
|
485
635
|
labelledBy
|
|
486
636
|
}) {
|
|
637
|
+
const strings = useStrings();
|
|
638
|
+
const announce = useFilloAnnounce();
|
|
487
639
|
const [open, setOpen] = useState2(false);
|
|
488
640
|
const [query, setQuery] = useState2("");
|
|
489
641
|
const [active, setActive] = useState2(0);
|
|
@@ -492,19 +644,22 @@ function CountrySelect({
|
|
|
492
644
|
const triggerRef = useRef3(null);
|
|
493
645
|
const popoverRef = useRef3(null);
|
|
494
646
|
const searchRef = useRef3(null);
|
|
647
|
+
const seedRef = useRef3(null);
|
|
495
648
|
const listId = `${labelledBy}-countries`;
|
|
496
649
|
const matches = useMemo3(() => {
|
|
497
650
|
const q = query.trim().toLowerCase();
|
|
498
|
-
if (!q) return
|
|
651
|
+
if (!q) return PHONE_PICKER_COUNTRIES;
|
|
499
652
|
const qDigits = q.replace(/[^\d]/g, "");
|
|
500
|
-
return
|
|
653
|
+
return PHONE_PICKER_COUNTRIES.filter(
|
|
501
654
|
(c) => c.name.toLowerCase().includes(q) || c.iso2.toLowerCase() === q || qDigits.length > 0 && c.dialCode.startsWith(qDigits)
|
|
502
655
|
);
|
|
503
656
|
}, [query]);
|
|
504
657
|
useEffect3(() => {
|
|
505
658
|
if (!open) return;
|
|
506
|
-
|
|
507
|
-
|
|
659
|
+
const seed = seedRef.current ?? "";
|
|
660
|
+
seedRef.current = null;
|
|
661
|
+
setQuery(seed);
|
|
662
|
+
setActive(seed ? 0 : Math.max(0, PHONE_PICKER_COUNTRIES.indexOf(country)));
|
|
508
663
|
const id = requestAnimationFrame(() => searchRef.current?.focus());
|
|
509
664
|
return () => cancelAnimationFrame(id);
|
|
510
665
|
}, [open, country]);
|
|
@@ -531,8 +686,15 @@ function CountrySelect({
|
|
|
531
686
|
if (!open) return;
|
|
532
687
|
document.getElementById(`${listId}-opt-${active}`)?.scrollIntoView({ block: "nearest" });
|
|
533
688
|
}, [active, open, listId]);
|
|
689
|
+
const resultsAnnouncement = strings.phoneResultsCount(matches.length);
|
|
690
|
+
useEffect3(() => {
|
|
691
|
+
if (!open || !query.trim()) return;
|
|
692
|
+
const id = setTimeout(() => announce(resultsAnnouncement), 300);
|
|
693
|
+
return () => clearTimeout(id);
|
|
694
|
+
}, [open, query, resultsAnnouncement, announce]);
|
|
534
695
|
function choose(c) {
|
|
535
696
|
if (!c) return;
|
|
697
|
+
announce(strings.phoneCountrySelected(c.name));
|
|
536
698
|
onPick(c);
|
|
537
699
|
setOpen(false);
|
|
538
700
|
}
|
|
@@ -552,8 +714,31 @@ function CountrySelect({
|
|
|
552
714
|
triggerRef.current?.focus();
|
|
553
715
|
}
|
|
554
716
|
}
|
|
555
|
-
|
|
556
|
-
|
|
717
|
+
function onTriggerKeyDown(e) {
|
|
718
|
+
if (e.key === "ArrowDown" || e.key === "ArrowUp") {
|
|
719
|
+
e.preventDefault();
|
|
720
|
+
setOpen(true);
|
|
721
|
+
return;
|
|
722
|
+
}
|
|
723
|
+
if (e.key.length === 1 && e.key !== " " && !e.ctrlKey && !e.altKey && !e.metaKey) {
|
|
724
|
+
e.preventDefault();
|
|
725
|
+
seedRef.current = e.key;
|
|
726
|
+
setOpen(true);
|
|
727
|
+
}
|
|
728
|
+
}
|
|
729
|
+
function onCompositeBlur(e) {
|
|
730
|
+
if (!open) return;
|
|
731
|
+
const next = e.relatedTarget;
|
|
732
|
+
if (next) {
|
|
733
|
+
if (!rootRef.current?.contains(next)) setOpen(false);
|
|
734
|
+
return;
|
|
735
|
+
}
|
|
736
|
+
queueMicrotask(() => {
|
|
737
|
+
if (!rootRef.current?.contains(document.activeElement)) setOpen(false);
|
|
738
|
+
});
|
|
739
|
+
}
|
|
740
|
+
return /* @__PURE__ */ jsxs2("div", { className: "fillo-phone-country", ref: rootRef, onBlur: onCompositeBlur, children: [
|
|
741
|
+
/* @__PURE__ */ jsxs2(
|
|
557
742
|
"button",
|
|
558
743
|
{
|
|
559
744
|
ref: triggerRef,
|
|
@@ -564,18 +749,19 @@ function CountrySelect({
|
|
|
564
749
|
"aria-controls": open ? listId : void 0,
|
|
565
750
|
"aria-label": `Country: ${country.name} (+${country.dialCode})`,
|
|
566
751
|
onClick: () => setOpen((o) => !o),
|
|
752
|
+
onKeyDown: onTriggerKeyDown,
|
|
567
753
|
children: [
|
|
568
|
-
/* @__PURE__ */
|
|
569
|
-
/* @__PURE__ */
|
|
754
|
+
/* @__PURE__ */ jsx2("span", { className: "fillo-phone-flag-emoji", "aria-hidden": "true", children: flagEmoji(country.iso2) }),
|
|
755
|
+
/* @__PURE__ */ jsxs2("span", { className: "fillo-phone-dial", children: [
|
|
570
756
|
"+",
|
|
571
757
|
country.dialCode
|
|
572
758
|
] }),
|
|
573
|
-
/* @__PURE__ */
|
|
759
|
+
/* @__PURE__ */ jsx2("span", { className: "fillo-phone-caret", "aria-hidden": "true", children: "\u25BE" })
|
|
574
760
|
]
|
|
575
761
|
}
|
|
576
762
|
),
|
|
577
|
-
open && /* @__PURE__ */
|
|
578
|
-
/* @__PURE__ */
|
|
763
|
+
open && /* @__PURE__ */ jsxs2("div", { ref: popoverRef, className: `fillo-phone-popover fillo-phone-popover--${placement}`, children: [
|
|
764
|
+
/* @__PURE__ */ jsx2(
|
|
579
765
|
"input",
|
|
580
766
|
{
|
|
581
767
|
ref: searchRef,
|
|
@@ -596,8 +782,8 @@ function CountrySelect({
|
|
|
596
782
|
onKeyDown: onSearchKey
|
|
597
783
|
}
|
|
598
784
|
),
|
|
599
|
-
/* @__PURE__ */
|
|
600
|
-
matches.map((c, i) => /* @__PURE__ */
|
|
785
|
+
/* @__PURE__ */ jsxs2("ul", { className: "fillo-phone-list", role: "listbox", id: listId, "aria-label": "Country", children: [
|
|
786
|
+
matches.map((c, i) => /* @__PURE__ */ jsxs2(
|
|
601
787
|
"li",
|
|
602
788
|
{
|
|
603
789
|
id: `${listId}-opt-${i}`,
|
|
@@ -610,9 +796,9 @@ function CountrySelect({
|
|
|
610
796
|
choose(c);
|
|
611
797
|
},
|
|
612
798
|
children: [
|
|
613
|
-
/* @__PURE__ */
|
|
614
|
-
/* @__PURE__ */
|
|
615
|
-
/* @__PURE__ */
|
|
799
|
+
/* @__PURE__ */ jsx2("span", { className: "fillo-phone-flag-emoji", "aria-hidden": "true", children: flagEmoji(c.iso2) }),
|
|
800
|
+
/* @__PURE__ */ jsx2("span", { className: "fillo-phone-option-name", children: c.name }),
|
|
801
|
+
/* @__PURE__ */ jsxs2("span", { className: "fillo-phone-option-dial", children: [
|
|
616
802
|
"+",
|
|
617
803
|
c.dialCode
|
|
618
804
|
] })
|
|
@@ -620,7 +806,7 @@ function CountrySelect({
|
|
|
620
806
|
},
|
|
621
807
|
c.iso2
|
|
622
808
|
)),
|
|
623
|
-
matches.length === 0 && /* @__PURE__ */
|
|
809
|
+
matches.length === 0 && /* @__PURE__ */ jsx2("li", { className: "fillo-phone-empty", role: "option", "aria-disabled": "true", children: "No matches" })
|
|
624
810
|
] })
|
|
625
811
|
] })
|
|
626
812
|
] });
|
|
@@ -628,13 +814,14 @@ function CountrySelect({
|
|
|
628
814
|
|
|
629
815
|
// src/signature.tsx
|
|
630
816
|
import { useEffect as useEffect4, useRef as useRef4, useState as useState3 } from "react";
|
|
631
|
-
import { jsx as
|
|
817
|
+
import { jsx as jsx3, jsxs as jsxs3 } from "react/jsx-runtime";
|
|
632
818
|
function SignatureField({ field, value, error, setValue, ids: providedIds }) {
|
|
633
819
|
const ids = useFilloFieldIds(field.id, providedIds);
|
|
634
820
|
const slots = useFieldSlots(field, error);
|
|
635
821
|
const strings = useStrings();
|
|
636
822
|
const typedInputId = `${ids.inputId}-typed`;
|
|
637
823
|
const canvasRef = useRef4(null);
|
|
824
|
+
const typedInputRef = useRef4(null);
|
|
638
825
|
const drawing = useRef4(false);
|
|
639
826
|
const [hasInk, setHasInk] = useState3(Boolean(value));
|
|
640
827
|
const [typedName, setTypedName] = useState3("");
|
|
@@ -731,28 +918,30 @@ function SignatureField({ field, value, error, setValue, ids: providedIds }) {
|
|
|
731
918
|
setHasInk(false);
|
|
732
919
|
setTypedName("");
|
|
733
920
|
setValue(null);
|
|
921
|
+
typedInputRef.current?.focus();
|
|
734
922
|
}
|
|
735
923
|
const describedBy = [field.description ? ids.descriptionId : null, error ? ids.errorId : null].filter(Boolean).join(" ") || void 0;
|
|
736
|
-
return /* @__PURE__ */
|
|
737
|
-
/* @__PURE__ */
|
|
924
|
+
return /* @__PURE__ */ jsxs3("div", { ...slots.wrapperProps(`fillo-field fillo-field--signature${error ? " fillo-field--error" : ""}`), children: [
|
|
925
|
+
/* @__PURE__ */ jsxs3("span", { className: slots.label, "data-fillo": "label", id: ids.labelId, children: [
|
|
738
926
|
field.label,
|
|
739
|
-
!field.required && /* @__PURE__ */
|
|
927
|
+
!field.required && /* @__PURE__ */ jsx3("span", { className: "fillo-optional", children: strings.optional })
|
|
740
928
|
] }),
|
|
741
|
-
field.description && /* @__PURE__ */
|
|
742
|
-
/* @__PURE__ */
|
|
929
|
+
field.description && /* @__PURE__ */ jsx3("p", { className: slots.description, "data-fillo": "fieldDescription", id: ids.descriptionId, children: field.description }),
|
|
930
|
+
/* @__PURE__ */ jsxs3(
|
|
743
931
|
"div",
|
|
744
932
|
{
|
|
745
933
|
role: "group",
|
|
746
934
|
"aria-labelledby": ids.labelId,
|
|
747
935
|
"aria-describedby": describedBy,
|
|
748
936
|
children: [
|
|
749
|
-
/* @__PURE__ */
|
|
750
|
-
/* @__PURE__ */
|
|
937
|
+
/* @__PURE__ */ jsxs3("div", { className: slots.control("fillo-signature"), "data-fillo": "control", children: [
|
|
938
|
+
/* @__PURE__ */ jsx3(
|
|
751
939
|
"canvas",
|
|
752
940
|
{
|
|
753
941
|
ref: canvasRef,
|
|
754
942
|
className: "fillo-signature-canvas",
|
|
755
|
-
|
|
943
|
+
role: "img",
|
|
944
|
+
"aria-label": hasInk ? strings.signatureSigned : strings.signatureEmpty,
|
|
756
945
|
onPointerDown: start,
|
|
757
946
|
onPointerMove: move,
|
|
758
947
|
onPointerUp: end,
|
|
@@ -760,15 +949,16 @@ function SignatureField({ field, value, error, setValue, ids: providedIds }) {
|
|
|
760
949
|
onPointerLeave: end
|
|
761
950
|
}
|
|
762
951
|
),
|
|
763
|
-
hasInk && /* @__PURE__ */
|
|
764
|
-
!hasInk && /* @__PURE__ */
|
|
952
|
+
hasInk && /* @__PURE__ */ jsx3("button", { type: "button", className: "fillo-signature-clear", onClick: clear, children: "Clear" }),
|
|
953
|
+
!hasInk && /* @__PURE__ */ jsx3("span", { className: "fillo-signature-hint", "aria-hidden": "true", children: "Sign here" })
|
|
765
954
|
] }),
|
|
766
|
-
/* @__PURE__ */
|
|
767
|
-
/* @__PURE__ */
|
|
768
|
-
/* @__PURE__ */
|
|
955
|
+
/* @__PURE__ */ jsxs3("div", { className: "fillo-signature-type", children: [
|
|
956
|
+
/* @__PURE__ */ jsx3("label", { className: "fillo-signature-type-label", htmlFor: typedInputId, children: "Or type your full name to sign" }),
|
|
957
|
+
/* @__PURE__ */ jsx3(
|
|
769
958
|
"input",
|
|
770
959
|
{
|
|
771
960
|
id: typedInputId,
|
|
961
|
+
ref: typedInputRef,
|
|
772
962
|
type: "text",
|
|
773
963
|
className: "fillo-input fillo-signature-type-input",
|
|
774
964
|
autoComplete: "name",
|
|
@@ -787,13 +977,13 @@ function SignatureField({ field, value, error, setValue, ids: providedIds }) {
|
|
|
787
977
|
]
|
|
788
978
|
}
|
|
789
979
|
),
|
|
790
|
-
error && /* @__PURE__ */
|
|
980
|
+
error && /* @__PURE__ */ jsx3("p", { className: slots.error, "data-fillo": "error", id: ids.errorId, children: error })
|
|
791
981
|
] });
|
|
792
982
|
}
|
|
793
983
|
|
|
794
984
|
// src/upload.tsx
|
|
795
985
|
import { useEffect as useEffect5, useRef as useRef5, useState as useState4 } from "react";
|
|
796
|
-
import { Fragment, jsx as
|
|
986
|
+
import { Fragment as Fragment2, jsx as jsx4, jsxs as jsxs4 } from "react/jsx-runtime";
|
|
797
987
|
function uploadStatus(inFlight, doneCount, strings) {
|
|
798
988
|
const failed = inFlight.filter((f) => f.error).length;
|
|
799
989
|
const active = inFlight.length - failed;
|
|
@@ -809,10 +999,19 @@ function formatBytes(bytes) {
|
|
|
809
999
|
if (bytes < 1024 ** 3) return `${(bytes / 1024 ** 2).toFixed(1)} MB`;
|
|
810
1000
|
return `${(bytes / 1024 ** 3).toFixed(2)} GB`;
|
|
811
1001
|
}
|
|
1002
|
+
function uploadFailureMessage(error, strings) {
|
|
1003
|
+
if (error instanceof Error && error.name === "FilloError") {
|
|
1004
|
+
const status = error.status;
|
|
1005
|
+
if (status !== void 0 && status >= 500) return strings.uploadUnavailable;
|
|
1006
|
+
return error.message || strings.uploadFailed;
|
|
1007
|
+
}
|
|
1008
|
+
return strings.uploadFailed;
|
|
1009
|
+
}
|
|
812
1010
|
function FileUploadField({ field, value, error, setValue, api, ids: providedIds }) {
|
|
813
1011
|
const ids = useFilloFieldIds(field.id, providedIds);
|
|
814
1012
|
const slots = useFieldSlots(field, error);
|
|
815
1013
|
const strings = useStrings();
|
|
1014
|
+
const chrome = useFilloChrome();
|
|
816
1015
|
const schema = field;
|
|
817
1016
|
const inputRef = useRef5(null);
|
|
818
1017
|
const [inFlight, setInFlight] = useState4([]);
|
|
@@ -834,9 +1033,12 @@ function FileUploadField({ field, value, error, setValue, api, ids: providedIds
|
|
|
834
1033
|
const filesRef = useRef5(files);
|
|
835
1034
|
filesRef.current = files;
|
|
836
1035
|
const maxFiles = schema.maxFiles ?? 1;
|
|
837
|
-
const
|
|
1036
|
+
const configuredMaxMb = schema.maxFileSizeMb ?? 500;
|
|
1037
|
+
const maxMb = chrome?.uploadFileSizeLimitMb ? Math.min(configuredMaxMb, chrome.uploadFileSizeLimitMb) : configuredMaxMb;
|
|
838
1038
|
const maxBytes = maxMb * 1024 * 1024;
|
|
839
|
-
const
|
|
1039
|
+
const storageBlocked = chrome?.uploadsAvailable === false;
|
|
1040
|
+
const storageUrl = storageBlocked && chrome?.devChrome ? safeHttpUrl(chrome.warningUrl) : null;
|
|
1041
|
+
const canUpload = Boolean(api.client && api.formId) && !storageBlocked;
|
|
840
1042
|
const activeInFlight = inFlight.filter((f) => !f.error).length;
|
|
841
1043
|
const remaining = maxFiles - files.length - activeInFlight;
|
|
842
1044
|
async function startUpload(file) {
|
|
@@ -871,7 +1073,7 @@ function FileUploadField({ field, value, error, setValue, api, ids: providedIds
|
|
|
871
1073
|
setValue(next);
|
|
872
1074
|
} catch (err) {
|
|
873
1075
|
if (mountedRef.current) {
|
|
874
|
-
const message = err
|
|
1076
|
+
const message = uploadFailureMessage(err, strings);
|
|
875
1077
|
setInFlight(
|
|
876
1078
|
(prev) => prev.map((f) => f.key === key ? { ...f, error: message } : f)
|
|
877
1079
|
);
|
|
@@ -898,13 +1100,13 @@ function FileUploadField({ field, value, error, setValue, api, ids: providedIds
|
|
|
898
1100
|
setValue(next);
|
|
899
1101
|
}
|
|
900
1102
|
const describedBy = [field.description ? ids.descriptionId : null, error ? ids.errorId : null].filter(Boolean).join(" ") || void 0;
|
|
901
|
-
return /* @__PURE__ */
|
|
902
|
-
/* @__PURE__ */
|
|
1103
|
+
return /* @__PURE__ */ jsxs4("div", { ...slots.wrapperProps(`fillo-field fillo-field--file_upload${error ? " fillo-field--error" : ""}`), children: [
|
|
1104
|
+
/* @__PURE__ */ jsxs4("label", { className: slots.label, "data-fillo": "label", id: ids.labelId, htmlFor: ids.inputId, children: [
|
|
903
1105
|
field.label,
|
|
904
|
-
!field.required && /* @__PURE__ */
|
|
1106
|
+
!field.required && /* @__PURE__ */ jsx4("span", { className: "fillo-optional", children: strings.optional })
|
|
905
1107
|
] }),
|
|
906
|
-
field.description && /* @__PURE__ */
|
|
907
|
-
remaining > 0 && /* @__PURE__ */
|
|
1108
|
+
field.description && /* @__PURE__ */ jsx4("p", { className: slots.description, "data-fillo": "fieldDescription", id: ids.descriptionId, children: field.description }),
|
|
1109
|
+
remaining > 0 && /* @__PURE__ */ jsxs4(
|
|
908
1110
|
"div",
|
|
909
1111
|
{
|
|
910
1112
|
"data-fillo": "control",
|
|
@@ -935,7 +1137,7 @@ function FileUploadField({ field, value, error, setValue, api, ids: providedIds
|
|
|
935
1137
|
}
|
|
936
1138
|
},
|
|
937
1139
|
children: [
|
|
938
|
-
/* @__PURE__ */
|
|
1140
|
+
/* @__PURE__ */ jsx4(
|
|
939
1141
|
"input",
|
|
940
1142
|
{
|
|
941
1143
|
ref: inputRef,
|
|
@@ -948,14 +1150,20 @@ function FileUploadField({ field, value, error, setValue, api, ids: providedIds
|
|
|
948
1150
|
onChange: (e) => canUpload && handleFiles(e.target.files)
|
|
949
1151
|
}
|
|
950
1152
|
),
|
|
951
|
-
canUpload ? /* @__PURE__ */
|
|
952
|
-
/* @__PURE__ */
|
|
953
|
-
/* @__PURE__ */
|
|
954
|
-
] }) : /* @__PURE__ */
|
|
1153
|
+
canUpload ? /* @__PURE__ */ jsxs4(Fragment2, { children: [
|
|
1154
|
+
/* @__PURE__ */ jsx4("span", { className: "fillo-dropzone-title", children: strings.dropzoneTitle(maxFiles > 1) }),
|
|
1155
|
+
/* @__PURE__ */ jsx4("span", { className: "fillo-dropzone-hint", children: strings.dropzoneHint(maxMb) })
|
|
1156
|
+
] }) : storageBlocked && chrome?.devChrome ? /* @__PURE__ */ jsxs4("span", { className: "fillo-dropzone-hint", children: [
|
|
1157
|
+
"Connect file storage to enable uploads",
|
|
1158
|
+
storageUrl && /* @__PURE__ */ jsxs4(Fragment2, { children: [
|
|
1159
|
+
" \u2014 ",
|
|
1160
|
+
/* @__PURE__ */ jsx4("a", { href: storageUrl, target: "_blank", rel: "noopener noreferrer", children: storageUrl })
|
|
1161
|
+
] })
|
|
1162
|
+
] }) : /* @__PURE__ */ jsx4("span", { className: "fillo-dropzone-hint", children: storageBlocked ? strings.uploadsUnavailable : strings.uploadsDisabled })
|
|
955
1163
|
]
|
|
956
1164
|
}
|
|
957
1165
|
),
|
|
958
|
-
/* @__PURE__ */
|
|
1166
|
+
/* @__PURE__ */ jsx4(
|
|
959
1167
|
"div",
|
|
960
1168
|
{
|
|
961
1169
|
role: "status",
|
|
@@ -965,11 +1173,12 @@ function FileUploadField({ field, value, error, setValue, api, ids: providedIds
|
|
|
965
1173
|
children: uploadStatus(inFlight, files.length, strings)
|
|
966
1174
|
}
|
|
967
1175
|
),
|
|
968
|
-
(inFlight.length > 0 || files.length > 0) && /* @__PURE__ */
|
|
969
|
-
files.map((f) => /* @__PURE__ */
|
|
970
|
-
/* @__PURE__ */
|
|
971
|
-
/* @__PURE__ */
|
|
972
|
-
/* @__PURE__ */
|
|
1176
|
+
(inFlight.length > 0 || files.length > 0) && /* @__PURE__ */ jsxs4("ul", { className: "fillo-files", children: [
|
|
1177
|
+
files.map((f) => /* @__PURE__ */ jsxs4("li", { className: "fillo-file fillo-file--done", children: [
|
|
1178
|
+
/* @__PURE__ */ jsx4("span", { className: "fillo-file-name", children: f.name }),
|
|
1179
|
+
/* @__PURE__ */ jsx4("span", { className: "fillo-file-meta", children: formatBytes(f.size) }),
|
|
1180
|
+
/* @__PURE__ */ jsx4("span", { className: "fillo-sr-only", children: "Uploaded" }),
|
|
1181
|
+
/* @__PURE__ */ jsx4(
|
|
973
1182
|
"button",
|
|
974
1183
|
{
|
|
975
1184
|
type: "button",
|
|
@@ -980,7 +1189,7 @@ function FileUploadField({ field, value, error, setValue, api, ids: providedIds
|
|
|
980
1189
|
}
|
|
981
1190
|
)
|
|
982
1191
|
] }, f.fileId)),
|
|
983
|
-
inFlight.map((f) => /* @__PURE__ */
|
|
1192
|
+
inFlight.map((f) => /* @__PURE__ */ jsxs4(
|
|
984
1193
|
"li",
|
|
985
1194
|
{
|
|
986
1195
|
className: `fillo-file${f.error ? " fillo-file--failed" : ""}`,
|
|
@@ -992,42 +1201,44 @@ function FileUploadField({ field, value, error, setValue, api, ids: providedIds
|
|
|
992
1201
|
"aria-valuenow": Math.round(f.fraction * 100)
|
|
993
1202
|
},
|
|
994
1203
|
children: [
|
|
995
|
-
/* @__PURE__ */
|
|
996
|
-
f.error ? /* @__PURE__ */
|
|
997
|
-
/* @__PURE__ */
|
|
998
|
-
|
|
999
|
-
|
|
1000
|
-
|
|
1001
|
-
|
|
1002
|
-
|
|
1003
|
-
|
|
1004
|
-
|
|
1005
|
-
|
|
1006
|
-
|
|
1007
|
-
|
|
1008
|
-
|
|
1009
|
-
|
|
1010
|
-
|
|
1011
|
-
|
|
1012
|
-
|
|
1013
|
-
|
|
1014
|
-
|
|
1015
|
-
|
|
1016
|
-
|
|
1017
|
-
|
|
1018
|
-
|
|
1019
|
-
|
|
1020
|
-
|
|
1021
|
-
|
|
1022
|
-
|
|
1023
|
-
|
|
1024
|
-
/* @__PURE__ */
|
|
1204
|
+
/* @__PURE__ */ jsx4("span", { className: "fillo-file-name", children: f.name }),
|
|
1205
|
+
f.error ? /* @__PURE__ */ jsxs4(Fragment2, { children: [
|
|
1206
|
+
/* @__PURE__ */ jsxs4("span", { className: "fillo-file-actions", children: [
|
|
1207
|
+
f.file && !f.tooLarge && remaining > 0 && /* @__PURE__ */ jsx4(
|
|
1208
|
+
"button",
|
|
1209
|
+
{
|
|
1210
|
+
type: "button",
|
|
1211
|
+
className: "fillo-file-retry",
|
|
1212
|
+
"aria-label": `${strings.uploadRetry} ${f.name}`,
|
|
1213
|
+
onClick: () => {
|
|
1214
|
+
const retryFile = f.file;
|
|
1215
|
+
if (!retryFile) return;
|
|
1216
|
+
setInFlight((prev) => prev.filter((x) => x.key !== f.key));
|
|
1217
|
+
void startUpload(retryFile);
|
|
1218
|
+
},
|
|
1219
|
+
children: strings.uploadRetry
|
|
1220
|
+
}
|
|
1221
|
+
),
|
|
1222
|
+
/* @__PURE__ */ jsx4(
|
|
1223
|
+
"button",
|
|
1224
|
+
{
|
|
1225
|
+
type: "button",
|
|
1226
|
+
className: "fillo-file-remove",
|
|
1227
|
+
"aria-label": "Dismiss",
|
|
1228
|
+
onClick: () => setInFlight((prev) => prev.filter((x) => x.key !== f.key)),
|
|
1229
|
+
children: "\xD7"
|
|
1230
|
+
}
|
|
1231
|
+
)
|
|
1232
|
+
] }),
|
|
1233
|
+
/* @__PURE__ */ jsx4("span", { className: "fillo-file-error", role: "alert", children: f.error })
|
|
1234
|
+
] }) : /* @__PURE__ */ jsxs4(Fragment2, { children: [
|
|
1235
|
+
/* @__PURE__ */ jsxs4("span", { className: "fillo-file-meta", children: [
|
|
1025
1236
|
Math.round(f.fraction * 100),
|
|
1026
1237
|
"% of ",
|
|
1027
1238
|
formatBytes(f.size)
|
|
1028
1239
|
] }),
|
|
1029
|
-
/* @__PURE__ */
|
|
1030
|
-
/* @__PURE__ */
|
|
1240
|
+
/* @__PURE__ */ jsx4("span", { className: "fillo-progress", "aria-hidden": "true", children: /* @__PURE__ */ jsx4("span", { className: "fillo-progress-bar", style: { width: `${f.fraction * 100}%` } }) }),
|
|
1241
|
+
/* @__PURE__ */ jsx4(
|
|
1031
1242
|
"button",
|
|
1032
1243
|
{
|
|
1033
1244
|
type: "button",
|
|
@@ -1043,12 +1254,12 @@ function FileUploadField({ field, value, error, setValue, api, ids: providedIds
|
|
|
1043
1254
|
f.key
|
|
1044
1255
|
))
|
|
1045
1256
|
] }),
|
|
1046
|
-
error && /* @__PURE__ */
|
|
1257
|
+
error && /* @__PURE__ */ jsx4("p", { className: slots.error, "data-fillo": "error", id: ids.errorId, children: error })
|
|
1047
1258
|
] });
|
|
1048
1259
|
}
|
|
1049
1260
|
|
|
1050
1261
|
// src/fields.tsx
|
|
1051
|
-
import { jsx as
|
|
1262
|
+
import { jsx as jsx5, jsxs as jsxs5 } from "react/jsx-runtime";
|
|
1052
1263
|
function fieldAria(field, error, ids) {
|
|
1053
1264
|
const describedBy = [
|
|
1054
1265
|
field.description ? ids.descriptionId : null,
|
|
@@ -1072,9 +1283,6 @@ function useControl(base, field, error) {
|
|
|
1072
1283
|
"data-fillo": "control"
|
|
1073
1284
|
};
|
|
1074
1285
|
}
|
|
1075
|
-
function isNativeControlTarget(target) {
|
|
1076
|
-
return target instanceof HTMLElement && Boolean(target.closest("input, textarea, select, button"));
|
|
1077
|
-
}
|
|
1078
1286
|
function FieldShell({
|
|
1079
1287
|
field,
|
|
1080
1288
|
error,
|
|
@@ -1095,7 +1303,7 @@ function FieldShell({
|
|
|
1095
1303
|
const descriptionCls = useSlotClass("fillo-description", { slot: "fieldDescription", ...state });
|
|
1096
1304
|
const errorCls = useSlotClass("fillo-error", { slot: "error", ...state });
|
|
1097
1305
|
const strings = useStrings();
|
|
1098
|
-
return /* @__PURE__ */
|
|
1306
|
+
return /* @__PURE__ */ jsxs5(
|
|
1099
1307
|
"div",
|
|
1100
1308
|
{
|
|
1101
1309
|
className: fieldCls,
|
|
@@ -1105,13 +1313,13 @@ function FieldShell({
|
|
|
1105
1313
|
"data-invalid": error ? "" : void 0,
|
|
1106
1314
|
"data-required": field.required ? "" : void 0,
|
|
1107
1315
|
children: [
|
|
1108
|
-
/* @__PURE__ */
|
|
1316
|
+
/* @__PURE__ */ jsxs5("label", { className: labelCls, "data-fillo": "label", id: ids.labelId, htmlFor: ids.inputId, children: [
|
|
1109
1317
|
field.label,
|
|
1110
|
-
!field.required && /* @__PURE__ */
|
|
1318
|
+
!field.required && /* @__PURE__ */ jsx5("span", { className: "fillo-optional", children: strings.optional })
|
|
1111
1319
|
] }),
|
|
1112
|
-
field.description && /* @__PURE__ */
|
|
1320
|
+
field.description && /* @__PURE__ */ jsx5("p", { className: descriptionCls, "data-fillo": "fieldDescription", id: ids.descriptionId, children: field.description }),
|
|
1113
1321
|
children,
|
|
1114
|
-
error && /* @__PURE__ */
|
|
1322
|
+
error && /* @__PURE__ */ jsx5("p", { className: errorCls, "data-fillo": "error", id: ids.errorId, children: error })
|
|
1115
1323
|
]
|
|
1116
1324
|
}
|
|
1117
1325
|
);
|
|
@@ -1119,13 +1327,15 @@ function FieldShell({
|
|
|
1119
1327
|
function TextInput({ field, value, error, setValue, ids: providedIds }) {
|
|
1120
1328
|
const ids = useFilloFieldIds(field.id, providedIds);
|
|
1121
1329
|
const type = field.kind === "email" ? "email" : field.kind === "url" ? "url" : field.kind === "number" ? "number" : field.kind === "date" ? "date" : "text";
|
|
1330
|
+
const autoComplete = field.kind === "email" ? "email" : field.kind === "url" ? "url" : void 0;
|
|
1122
1331
|
const control = useControl("fillo-input", field, error);
|
|
1123
|
-
return /* @__PURE__ */
|
|
1332
|
+
return /* @__PURE__ */ jsx5(FieldShell, { field, error, ids, children: /* @__PURE__ */ jsx5(
|
|
1124
1333
|
"input",
|
|
1125
1334
|
{
|
|
1126
1335
|
id: ids.inputId,
|
|
1127
1336
|
...control,
|
|
1128
1337
|
type,
|
|
1338
|
+
autoComplete,
|
|
1129
1339
|
value: typeof value === "string" || typeof value === "number" ? String(value) : "",
|
|
1130
1340
|
placeholder: field.placeholder,
|
|
1131
1341
|
maxLength: "maxLength" in field ? field.maxLength : void 0,
|
|
@@ -1136,10 +1346,75 @@ function TextInput({ field, value, error, setValue, ids: providedIds }) {
|
|
|
1136
1346
|
}
|
|
1137
1347
|
) });
|
|
1138
1348
|
}
|
|
1349
|
+
function NumberInput({ field, value, error, setValue, api, ids: providedIds }) {
|
|
1350
|
+
const ids = useFilloFieldIds(field.id, providedIds);
|
|
1351
|
+
const control = useControl("fillo-input", field, error);
|
|
1352
|
+
const [draft, setDraft] = useState5(null);
|
|
1353
|
+
const inputRef = useRef6(null);
|
|
1354
|
+
const pendingCaret = useRef6(null);
|
|
1355
|
+
const [, setRevertTick] = useState5(0);
|
|
1356
|
+
useLayoutEffect2(() => {
|
|
1357
|
+
if (pendingCaret.current === null) return;
|
|
1358
|
+
const pos = pendingCaret.current;
|
|
1359
|
+
pendingCaret.current = null;
|
|
1360
|
+
inputRef.current?.setSelectionRange(pos, pos);
|
|
1361
|
+
});
|
|
1362
|
+
const num = field;
|
|
1363
|
+
const grouped = num.notation === "grouped";
|
|
1364
|
+
const hasAffix = Boolean(num.prefix) || Boolean(num.suffix);
|
|
1365
|
+
if (!grouped && !hasAffix) {
|
|
1366
|
+
return /* @__PURE__ */ jsx5(
|
|
1367
|
+
TextInput,
|
|
1368
|
+
{
|
|
1369
|
+
field,
|
|
1370
|
+
value,
|
|
1371
|
+
error,
|
|
1372
|
+
setValue,
|
|
1373
|
+
api,
|
|
1374
|
+
ids: providedIds
|
|
1375
|
+
}
|
|
1376
|
+
);
|
|
1377
|
+
}
|
|
1378
|
+
const stored = typeof value === "string" || typeof value === "number" ? String(value) : "";
|
|
1379
|
+
const n = Number(stored);
|
|
1380
|
+
const display = draft !== null ? draft : grouped && stored !== "" && Number.isFinite(n) ? formatGroupedNumber(n, { decimals: num.decimals }) : stored;
|
|
1381
|
+
const input = /* @__PURE__ */ jsx5(
|
|
1382
|
+
"input",
|
|
1383
|
+
{
|
|
1384
|
+
ref: inputRef,
|
|
1385
|
+
id: ids.inputId,
|
|
1386
|
+
...control,
|
|
1387
|
+
type: "text",
|
|
1388
|
+
inputMode: "decimal",
|
|
1389
|
+
value: display,
|
|
1390
|
+
placeholder: field.placeholder,
|
|
1391
|
+
onBlur: grouped ? () => setDraft(null) : void 0,
|
|
1392
|
+
onChange: (e) => {
|
|
1393
|
+
if (!isValidPartialNumberText(e.target.value)) {
|
|
1394
|
+
pendingCaret.current = e.target.selectionStart ?? e.target.value.length;
|
|
1395
|
+
setRevertTick((t) => t + 1);
|
|
1396
|
+
return;
|
|
1397
|
+
}
|
|
1398
|
+
if (grouped) {
|
|
1399
|
+
setDraft(e.target.value);
|
|
1400
|
+
setValue(parseGroupedNumber(e.target.value));
|
|
1401
|
+
} else {
|
|
1402
|
+
setValue(e.target.value);
|
|
1403
|
+
}
|
|
1404
|
+
},
|
|
1405
|
+
...fieldAria(field, error, ids)
|
|
1406
|
+
}
|
|
1407
|
+
);
|
|
1408
|
+
return /* @__PURE__ */ jsx5(FieldShell, { field, error, ids, children: hasAffix ? /* @__PURE__ */ jsxs5("div", { className: "fillo-number", children: [
|
|
1409
|
+
num.prefix && /* @__PURE__ */ jsx5("span", { className: "fillo-number-prefix", children: num.prefix }),
|
|
1410
|
+
input,
|
|
1411
|
+
num.suffix && /* @__PURE__ */ jsx5("span", { className: "fillo-number-suffix", children: num.suffix })
|
|
1412
|
+
] }) : input });
|
|
1413
|
+
}
|
|
1139
1414
|
function LongText({ field, value, error, setValue, ids: providedIds }) {
|
|
1140
1415
|
const ids = useFilloFieldIds(field.id, providedIds);
|
|
1141
1416
|
const control = useControl("fillo-input fillo-textarea", field, error);
|
|
1142
|
-
return /* @__PURE__ */
|
|
1417
|
+
return /* @__PURE__ */ jsx5(FieldShell, { field, error, ids, children: /* @__PURE__ */ jsx5(
|
|
1143
1418
|
"textarea",
|
|
1144
1419
|
{
|
|
1145
1420
|
id: ids.inputId,
|
|
@@ -1176,7 +1451,7 @@ var OPTION_ICON_PATHS = {
|
|
|
1176
1451
|
};
|
|
1177
1452
|
function ChoiceIcon({ icon }) {
|
|
1178
1453
|
if (!icon) return null;
|
|
1179
|
-
return /* @__PURE__ */
|
|
1454
|
+
return /* @__PURE__ */ jsx5(
|
|
1180
1455
|
"svg",
|
|
1181
1456
|
{
|
|
1182
1457
|
className: "fillo-option-icon",
|
|
@@ -1187,7 +1462,7 @@ function ChoiceIcon({ icon }) {
|
|
|
1187
1462
|
strokeLinecap: "round",
|
|
1188
1463
|
strokeLinejoin: "round",
|
|
1189
1464
|
"aria-hidden": "true",
|
|
1190
|
-
children: /* @__PURE__ */
|
|
1465
|
+
children: /* @__PURE__ */ jsx5("path", { d: OPTION_ICON_PATHS[icon] })
|
|
1191
1466
|
}
|
|
1192
1467
|
);
|
|
1193
1468
|
}
|
|
@@ -1201,7 +1476,7 @@ function SingleChoice({ field, value, error, setValue, ids: providedIds }) {
|
|
|
1201
1476
|
const isOtherValue = typeof value === "string" && value !== "" && !choice.options.some((o) => o.id === value);
|
|
1202
1477
|
const [otherOn, setOtherOn] = useState5(isOtherValue);
|
|
1203
1478
|
const otherActive = isOtherValue || value === "" && otherOn;
|
|
1204
|
-
return /* @__PURE__ */
|
|
1479
|
+
return /* @__PURE__ */ jsx5(FieldShell, { field, error, ids, children: /* @__PURE__ */ jsxs5(
|
|
1205
1480
|
"div",
|
|
1206
1481
|
{
|
|
1207
1482
|
className: slotClass2("fillo-options", appearance, { slot: "options", kind: field.kind, fieldId: field.id }),
|
|
@@ -1210,7 +1485,7 @@ function SingleChoice({ field, value, error, setValue, ids: providedIds }) {
|
|
|
1210
1485
|
"aria-labelledby": ids.labelId,
|
|
1211
1486
|
...fieldAria(field, error, ids),
|
|
1212
1487
|
children: [
|
|
1213
|
-
options.map((opt) => /* @__PURE__ */
|
|
1488
|
+
options.map((opt) => /* @__PURE__ */ jsxs5(
|
|
1214
1489
|
"label",
|
|
1215
1490
|
{
|
|
1216
1491
|
"data-option": opt.id,
|
|
@@ -1221,14 +1496,8 @@ function SingleChoice({ field, value, error, setValue, ids: providedIds }) {
|
|
|
1221
1496
|
opt.id,
|
|
1222
1497
|
value === opt.id
|
|
1223
1498
|
),
|
|
1224
|
-
onClick: (event) => {
|
|
1225
|
-
if (isNativeControlTarget(event.target)) return;
|
|
1226
|
-
event.preventDefault();
|
|
1227
|
-
setOtherOn(false);
|
|
1228
|
-
setValue(opt.id);
|
|
1229
|
-
},
|
|
1230
1499
|
children: [
|
|
1231
|
-
/* @__PURE__ */
|
|
1500
|
+
/* @__PURE__ */ jsx5(
|
|
1232
1501
|
"input",
|
|
1233
1502
|
{
|
|
1234
1503
|
type: "radio",
|
|
@@ -1241,16 +1510,22 @@ function SingleChoice({ field, value, error, setValue, ids: providedIds }) {
|
|
|
1241
1510
|
}
|
|
1242
1511
|
}
|
|
1243
1512
|
),
|
|
1244
|
-
/* @__PURE__ */
|
|
1245
|
-
|
|
1513
|
+
/* @__PURE__ */ jsx5(ChoiceIcon, { icon: opt.icon }),
|
|
1514
|
+
opt.icon && value === opt.id && // Icon-mode hides the native input, so its check state is
|
|
1515
|
+
// otherwise color-only (forfeits forced-colors' own indicator).
|
|
1516
|
+
/* @__PURE__ */ jsx5("span", { className: "fillo-option-check", "aria-hidden": "true", children: "\u2713" }),
|
|
1517
|
+
/* @__PURE__ */ jsx5("span", { className: slotClass2("fillo-option-label", appearance, { slot: "optionLabel", kind: field.kind, fieldId: field.id }), "data-fillo": "optionLabel", children: opt.label })
|
|
1246
1518
|
]
|
|
1247
1519
|
},
|
|
1248
1520
|
opt.id
|
|
1249
1521
|
)),
|
|
1250
|
-
choice.allowOther && //
|
|
1251
|
-
//
|
|
1252
|
-
|
|
1253
|
-
|
|
1522
|
+
choice.allowOther && // The outer element is a <label> so the whole row is a pointer
|
|
1523
|
+
// target (native label click-forwarding), same as every other
|
|
1524
|
+
// option row above. The free-text input below is its own labelable
|
|
1525
|
+
// descendant, so it handles its own clicks without re-toggling the
|
|
1526
|
+
// radio (dom's proven pattern).
|
|
1527
|
+
/* @__PURE__ */ jsxs5(
|
|
1528
|
+
"label",
|
|
1254
1529
|
{
|
|
1255
1530
|
"data-fillo": "option",
|
|
1256
1531
|
"data-selected": otherActive ? "" : void 0,
|
|
@@ -1260,8 +1535,8 @@ function SingleChoice({ field, value, error, setValue, ids: providedIds }) {
|
|
|
1260
1535
|
otherActive
|
|
1261
1536
|
),
|
|
1262
1537
|
children: [
|
|
1263
|
-
/* @__PURE__ */
|
|
1264
|
-
/* @__PURE__ */
|
|
1538
|
+
/* @__PURE__ */ jsxs5("span", { className: "fillo-option-main", children: [
|
|
1539
|
+
/* @__PURE__ */ jsx5(
|
|
1265
1540
|
"input",
|
|
1266
1541
|
{
|
|
1267
1542
|
type: "radio",
|
|
@@ -1274,9 +1549,9 @@ function SingleChoice({ field, value, error, setValue, ids: providedIds }) {
|
|
|
1274
1549
|
}
|
|
1275
1550
|
}
|
|
1276
1551
|
),
|
|
1277
|
-
/* @__PURE__ */
|
|
1552
|
+
/* @__PURE__ */ jsx5("span", { className: slotClass2("fillo-option-label", appearance, { slot: "optionLabel", kind: field.kind, fieldId: field.id }), "data-fillo": "optionLabel", children: strings.other })
|
|
1278
1553
|
] }),
|
|
1279
|
-
otherActive && /* @__PURE__ */
|
|
1554
|
+
otherActive && /* @__PURE__ */ jsx5(
|
|
1280
1555
|
"input",
|
|
1281
1556
|
{
|
|
1282
1557
|
type: "text",
|
|
@@ -1308,7 +1583,7 @@ function MultiChoice({ field, value, error, setValue, ids: providedIds }) {
|
|
|
1308
1583
|
const toggle = (id) => setValue(selected.includes(id) ? selected.filter((v) => v !== id) : [...selected, id]);
|
|
1309
1584
|
const appearance = useFilloAppearance();
|
|
1310
1585
|
const optionCls = (base, optionId, isSelected) => slotClass2(base, appearance, { slot: "option", kind: field.kind, fieldId: field.id, optionId, selected: isSelected });
|
|
1311
|
-
return /* @__PURE__ */
|
|
1586
|
+
return /* @__PURE__ */ jsx5(FieldShell, { field, error, ids, children: /* @__PURE__ */ jsxs5(
|
|
1312
1587
|
"div",
|
|
1313
1588
|
{
|
|
1314
1589
|
className: slotClass2("fillo-options", appearance, { slot: "options", kind: field.kind, fieldId: field.id }),
|
|
@@ -1317,7 +1592,7 @@ function MultiChoice({ field, value, error, setValue, ids: providedIds }) {
|
|
|
1317
1592
|
"aria-labelledby": ids.labelId,
|
|
1318
1593
|
...fieldAria(field, error, ids),
|
|
1319
1594
|
children: [
|
|
1320
|
-
options.map((opt) => /* @__PURE__ */
|
|
1595
|
+
options.map((opt) => /* @__PURE__ */ jsxs5(
|
|
1321
1596
|
"label",
|
|
1322
1597
|
{
|
|
1323
1598
|
"data-option": opt.id,
|
|
@@ -1328,13 +1603,8 @@ function MultiChoice({ field, value, error, setValue, ids: providedIds }) {
|
|
|
1328
1603
|
opt.id,
|
|
1329
1604
|
selected.includes(opt.id)
|
|
1330
1605
|
),
|
|
1331
|
-
onClick: (event) => {
|
|
1332
|
-
if (isNativeControlTarget(event.target)) return;
|
|
1333
|
-
event.preventDefault();
|
|
1334
|
-
toggle(opt.id);
|
|
1335
|
-
},
|
|
1336
1606
|
children: [
|
|
1337
|
-
/* @__PURE__ */
|
|
1607
|
+
/* @__PURE__ */ jsx5(
|
|
1338
1608
|
"input",
|
|
1339
1609
|
{
|
|
1340
1610
|
type: "checkbox",
|
|
@@ -1343,16 +1613,22 @@ function MultiChoice({ field, value, error, setValue, ids: providedIds }) {
|
|
|
1343
1613
|
onChange: () => toggle(opt.id)
|
|
1344
1614
|
}
|
|
1345
1615
|
),
|
|
1346
|
-
/* @__PURE__ */
|
|
1347
|
-
|
|
1616
|
+
/* @__PURE__ */ jsx5(ChoiceIcon, { icon: opt.icon }),
|
|
1617
|
+
opt.icon && selected.includes(opt.id) && // Icon-mode hides the native input, so its check state is
|
|
1618
|
+
// otherwise color-only (forfeits forced-colors' own indicator).
|
|
1619
|
+
/* @__PURE__ */ jsx5("span", { className: "fillo-option-check", "aria-hidden": "true", children: "\u2713" }),
|
|
1620
|
+
/* @__PURE__ */ jsx5("span", { className: slotClass2("fillo-option-label", appearance, { slot: "optionLabel", kind: field.kind, fieldId: field.id }), "data-fillo": "optionLabel", children: opt.label })
|
|
1348
1621
|
]
|
|
1349
1622
|
},
|
|
1350
1623
|
opt.id
|
|
1351
1624
|
)),
|
|
1352
|
-
choice.allowOther && //
|
|
1353
|
-
//
|
|
1354
|
-
|
|
1355
|
-
|
|
1625
|
+
choice.allowOther && // The outer element is a <label> so the whole row is a pointer
|
|
1626
|
+
// target (native label click-forwarding), same as every other
|
|
1627
|
+
// option row above. The free-text input below is its own labelable
|
|
1628
|
+
// descendant, so it handles its own clicks without re-toggling the
|
|
1629
|
+
// checkbox (dom's proven pattern).
|
|
1630
|
+
/* @__PURE__ */ jsxs5(
|
|
1631
|
+
"label",
|
|
1356
1632
|
{
|
|
1357
1633
|
"data-fillo": "option",
|
|
1358
1634
|
"data-selected": otherActive ? "" : void 0,
|
|
@@ -1362,8 +1638,8 @@ function MultiChoice({ field, value, error, setValue, ids: providedIds }) {
|
|
|
1362
1638
|
otherActive
|
|
1363
1639
|
),
|
|
1364
1640
|
children: [
|
|
1365
|
-
/* @__PURE__ */
|
|
1366
|
-
/* @__PURE__ */
|
|
1641
|
+
/* @__PURE__ */ jsxs5("span", { className: "fillo-option-main", children: [
|
|
1642
|
+
/* @__PURE__ */ jsx5(
|
|
1367
1643
|
"input",
|
|
1368
1644
|
{
|
|
1369
1645
|
type: "checkbox",
|
|
@@ -1379,9 +1655,9 @@ function MultiChoice({ field, value, error, setValue, ids: providedIds }) {
|
|
|
1379
1655
|
}
|
|
1380
1656
|
}
|
|
1381
1657
|
),
|
|
1382
|
-
/* @__PURE__ */
|
|
1658
|
+
/* @__PURE__ */ jsx5("span", { className: slotClass2("fillo-option-label", appearance, { slot: "optionLabel", kind: field.kind, fieldId: field.id }), "data-fillo": "optionLabel", children: strings.other })
|
|
1383
1659
|
] }),
|
|
1384
|
-
otherActive && /* @__PURE__ */
|
|
1660
|
+
otherActive && /* @__PURE__ */ jsx5(
|
|
1385
1661
|
"input",
|
|
1386
1662
|
{
|
|
1387
1663
|
type: "text",
|
|
@@ -1419,9 +1695,9 @@ function Dropdown({ field, value, error, setValue, ids: providedIds }) {
|
|
|
1419
1695
|
const [otherOn, setOtherOn] = useState5(isOtherValue);
|
|
1420
1696
|
const otherActive = isOtherValue || value === "" && otherOn;
|
|
1421
1697
|
const control = useControl("fillo-input fillo-select", field, error);
|
|
1422
|
-
return /* @__PURE__ */
|
|
1423
|
-
/* @__PURE__ */
|
|
1424
|
-
/* @__PURE__ */
|
|
1698
|
+
return /* @__PURE__ */ jsxs5(FieldShell, { field, error, ids, children: [
|
|
1699
|
+
/* @__PURE__ */ jsxs5("div", { className: "fillo-select-wrap", children: [
|
|
1700
|
+
/* @__PURE__ */ jsxs5(
|
|
1425
1701
|
"select",
|
|
1426
1702
|
{
|
|
1427
1703
|
id: ids.inputId,
|
|
@@ -1438,15 +1714,15 @@ function Dropdown({ field, value, error, setValue, ids: providedIds }) {
|
|
|
1438
1714
|
}
|
|
1439
1715
|
},
|
|
1440
1716
|
children: [
|
|
1441
|
-
/* @__PURE__ */
|
|
1442
|
-
options.map((opt) => /* @__PURE__ */
|
|
1443
|
-
choice.allowOther && /* @__PURE__ */
|
|
1717
|
+
/* @__PURE__ */ jsx5("option", { value: "", children: field.placeholder ?? strings.choosePlaceholder }),
|
|
1718
|
+
options.map((opt) => /* @__PURE__ */ jsx5("option", { value: opt.id, children: opt.label }, opt.id)),
|
|
1719
|
+
choice.allowOther && /* @__PURE__ */ jsx5("option", { value: otherValue, children: `${strings.other}\u2026` })
|
|
1444
1720
|
]
|
|
1445
1721
|
}
|
|
1446
1722
|
),
|
|
1447
|
-
/* @__PURE__ */
|
|
1723
|
+
/* @__PURE__ */ jsx5("span", { className: "fillo-select-icon", "aria-hidden": "true" })
|
|
1448
1724
|
] }),
|
|
1449
|
-
otherActive && /* @__PURE__ */
|
|
1725
|
+
otherActive && /* @__PURE__ */ jsx5(
|
|
1450
1726
|
"input",
|
|
1451
1727
|
{
|
|
1452
1728
|
type: "text",
|
|
@@ -1467,48 +1743,46 @@ function Checkbox({ field, value, error, setValue, ids: providedIds }) {
|
|
|
1467
1743
|
const slots = useFieldSlots(field, error);
|
|
1468
1744
|
const checkbox = field;
|
|
1469
1745
|
if (checkbox.appearance === "toggle") {
|
|
1470
|
-
return /* @__PURE__ */
|
|
1746
|
+
return /* @__PURE__ */ jsxs5(
|
|
1471
1747
|
"div",
|
|
1472
1748
|
{
|
|
1473
1749
|
...slots.wrapperProps(
|
|
1474
1750
|
`fillo-field fillo-field--checkbox fillo-field--toggle${error ? " fillo-field--error" : ""}`
|
|
1475
1751
|
),
|
|
1476
1752
|
children: [
|
|
1477
|
-
/* @__PURE__ */
|
|
1478
|
-
/* @__PURE__ */
|
|
1753
|
+
/* @__PURE__ */ jsxs5("label", { className: "fillo-toggle", "data-fillo": "option", "data-checked": value === true ? "" : void 0, children: [
|
|
1754
|
+
/* @__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
1755
|
field.label,
|
|
1480
|
-
!field.required && /* @__PURE__ */
|
|
1756
|
+
!field.required && /* @__PURE__ */ jsx5("span", { className: "fillo-optional", children: strings.optional })
|
|
1481
1757
|
] }) }),
|
|
1482
|
-
/* @__PURE__ */
|
|
1758
|
+
/* @__PURE__ */ jsx5(
|
|
1483
1759
|
"input",
|
|
1484
1760
|
{
|
|
1485
1761
|
id: ids.inputId,
|
|
1486
1762
|
type: "checkbox",
|
|
1487
|
-
role: "switch",
|
|
1488
1763
|
className: "fillo-toggle-input",
|
|
1489
1764
|
checked: value === true,
|
|
1490
1765
|
onChange: (e) => setValue(e.target.checked),
|
|
1491
|
-
"aria-checked": value === true,
|
|
1492
1766
|
...fieldAria(field, error, ids)
|
|
1493
1767
|
}
|
|
1494
1768
|
),
|
|
1495
|
-
/* @__PURE__ */
|
|
1769
|
+
/* @__PURE__ */ jsx5("span", { className: "fillo-toggle-track", "aria-hidden": "true", children: /* @__PURE__ */ jsx5("span", { className: "fillo-toggle-thumb" }) })
|
|
1496
1770
|
] }),
|
|
1497
|
-
field.description && /* @__PURE__ */
|
|
1498
|
-
error && /* @__PURE__ */
|
|
1771
|
+
field.description && /* @__PURE__ */ jsx5("p", { className: slots.description, "data-fillo": "fieldDescription", id: ids.descriptionId, children: field.description }),
|
|
1772
|
+
error && /* @__PURE__ */ jsx5("p", { className: slots.error, "data-fillo": "error", id: ids.errorId, children: error })
|
|
1499
1773
|
]
|
|
1500
1774
|
}
|
|
1501
1775
|
);
|
|
1502
1776
|
}
|
|
1503
|
-
return /* @__PURE__ */
|
|
1777
|
+
return /* @__PURE__ */ jsxs5(
|
|
1504
1778
|
"div",
|
|
1505
1779
|
{
|
|
1506
1780
|
...slots.wrapperProps(
|
|
1507
1781
|
`fillo-field fillo-field--checkbox${error ? " fillo-field--error" : ""}`
|
|
1508
1782
|
),
|
|
1509
1783
|
children: [
|
|
1510
|
-
/* @__PURE__ */
|
|
1511
|
-
/* @__PURE__ */
|
|
1784
|
+
/* @__PURE__ */ jsxs5("label", { className: "fillo-option", "data-fillo": "option", "data-checked": value === true ? "" : void 0, children: [
|
|
1785
|
+
/* @__PURE__ */ jsx5(
|
|
1512
1786
|
"input",
|
|
1513
1787
|
{
|
|
1514
1788
|
id: ids.inputId,
|
|
@@ -1519,34 +1793,27 @@ function Checkbox({ field, value, error, setValue, ids: providedIds }) {
|
|
|
1519
1793
|
...fieldAria(field, error, ids)
|
|
1520
1794
|
}
|
|
1521
1795
|
),
|
|
1522
|
-
/* @__PURE__ */
|
|
1796
|
+
/* @__PURE__ */ jsxs5("span", { className: slotClass2("fillo-option-label", appearance, { slot: "optionLabel", kind: field.kind, fieldId: field.id }), "data-fillo": "optionLabel", children: [
|
|
1523
1797
|
field.label,
|
|
1524
|
-
!field.required && /* @__PURE__ */
|
|
1798
|
+
!field.required && /* @__PURE__ */ jsx5("span", { className: "fillo-optional", children: strings.optional })
|
|
1525
1799
|
] })
|
|
1526
1800
|
] }),
|
|
1527
|
-
field.description && /* @__PURE__ */
|
|
1528
|
-
error && /* @__PURE__ */
|
|
1801
|
+
field.description && /* @__PURE__ */ jsx5("p", { className: slots.description, "data-fillo": "fieldDescription", id: ids.descriptionId, children: field.description }),
|
|
1802
|
+
error && /* @__PURE__ */ jsx5("p", { className: slots.error, "data-fillo": "error", id: ids.errorId, children: error })
|
|
1529
1803
|
]
|
|
1530
1804
|
}
|
|
1531
1805
|
);
|
|
1532
1806
|
}
|
|
1533
1807
|
function radiogroupKeyDown(e, steps, current, setValue) {
|
|
1534
|
-
const
|
|
1535
|
-
const
|
|
1536
|
-
|
|
1808
|
+
const group = e.currentTarget;
|
|
1809
|
+
const rtl = getComputedStyle(group).direction === "rtl";
|
|
1810
|
+
const index = rovingIndex(steps, current);
|
|
1811
|
+
const next = radioGroupStep(e.key, index, steps.length, { rtl });
|
|
1812
|
+
if (next == null) return;
|
|
1537
1813
|
e.preventDefault();
|
|
1538
|
-
const index = current == null ? -1 : steps.indexOf(current);
|
|
1539
|
-
let next;
|
|
1540
|
-
if (index === -1) {
|
|
1541
|
-
next = forward ? 0 : steps.length - 1;
|
|
1542
|
-
} else {
|
|
1543
|
-
next = forward ? index + 1 : index - 1;
|
|
1544
|
-
next = (next + steps.length) % steps.length;
|
|
1545
|
-
}
|
|
1546
1814
|
const value = steps[next];
|
|
1547
1815
|
if (value === void 0) return;
|
|
1548
1816
|
setValue(value);
|
|
1549
|
-
const group = e.currentTarget;
|
|
1550
1817
|
const buttons = group.querySelectorAll('[role="radio"]');
|
|
1551
1818
|
buttons[next]?.focus();
|
|
1552
1819
|
}
|
|
@@ -1564,15 +1831,16 @@ function Rating({ field, value, error, setValue, ids: providedIds }) {
|
|
|
1564
1831
|
const tabbable = rovingIndex(steps, selected);
|
|
1565
1832
|
const appearance = useFilloAppearance();
|
|
1566
1833
|
const control = useControl("fillo-rating", field, error);
|
|
1567
|
-
return /* @__PURE__ */
|
|
1834
|
+
return /* @__PURE__ */ jsx5(FieldShell, { field, error, ids, children: /* @__PURE__ */ jsx5(
|
|
1568
1835
|
"div",
|
|
1569
1836
|
{
|
|
1837
|
+
id: ids.inputId,
|
|
1570
1838
|
...control,
|
|
1571
1839
|
role: "radiogroup",
|
|
1572
1840
|
"aria-labelledby": ids.labelId,
|
|
1573
1841
|
...fieldAria(field, error, ids),
|
|
1574
1842
|
onKeyDown: (e) => radiogroupKeyDown(e, steps, selected, (n) => setValue(n)),
|
|
1575
|
-
children: steps.map((n, i) => /* @__PURE__ */
|
|
1843
|
+
children: steps.map((n, i) => /* @__PURE__ */ jsx5(
|
|
1576
1844
|
"button",
|
|
1577
1845
|
{
|
|
1578
1846
|
type: "button",
|
|
@@ -1588,8 +1856,11 @@ function Rating({ field, value, error, setValue, ids: providedIds }) {
|
|
|
1588
1856
|
"aria-label": `${n} of ${max}`,
|
|
1589
1857
|
"aria-checked": n === current,
|
|
1590
1858
|
tabIndex: i === tabbable ? 0 : -1,
|
|
1591
|
-
onClick: () =>
|
|
1592
|
-
|
|
1859
|
+
onClick: (event) => {
|
|
1860
|
+
if (event.detail === 0 && n === current) return;
|
|
1861
|
+
setValue(n === current ? null : n);
|
|
1862
|
+
},
|
|
1863
|
+
children: n <= current ? "\u2605" : "\u2606"
|
|
1593
1864
|
},
|
|
1594
1865
|
n
|
|
1595
1866
|
))
|
|
@@ -1615,10 +1886,11 @@ function LinearScale({ field, value, error, setValue, ids: providedIds }) {
|
|
|
1615
1886
|
].filter(Boolean).join(" ") || void 0;
|
|
1616
1887
|
const appearance = useFilloAppearance();
|
|
1617
1888
|
const control = useControl("fillo-scale", field, error);
|
|
1618
|
-
return /* @__PURE__ */
|
|
1619
|
-
/* @__PURE__ */
|
|
1889
|
+
return /* @__PURE__ */ jsxs5(FieldShell, { field, error, ids, children: [
|
|
1890
|
+
/* @__PURE__ */ jsx5(
|
|
1620
1891
|
"div",
|
|
1621
1892
|
{
|
|
1893
|
+
id: ids.inputId,
|
|
1622
1894
|
...control,
|
|
1623
1895
|
role: "radiogroup",
|
|
1624
1896
|
"aria-labelledby": ids.labelId,
|
|
@@ -1626,7 +1898,7 @@ function LinearScale({ field, value, error, setValue, ids: providedIds }) {
|
|
|
1626
1898
|
"aria-required": field.required ? true : void 0,
|
|
1627
1899
|
"aria-describedby": describedBy,
|
|
1628
1900
|
onKeyDown: (e) => radiogroupKeyDown(e, steps, current, (n) => setValue(n)),
|
|
1629
|
-
children: steps.map((n, i) => /* @__PURE__ */
|
|
1901
|
+
children: steps.map((n, i) => /* @__PURE__ */ jsx5(
|
|
1630
1902
|
"button",
|
|
1631
1903
|
{
|
|
1632
1904
|
type: "button",
|
|
@@ -1642,68 +1914,86 @@ function LinearScale({ field, value, error, setValue, ids: providedIds }) {
|
|
|
1642
1914
|
"aria-label": String(n),
|
|
1643
1915
|
"aria-checked": value === n,
|
|
1644
1916
|
tabIndex: i === tabbable ? 0 : -1,
|
|
1645
|
-
onClick: () =>
|
|
1917
|
+
onClick: (event) => {
|
|
1918
|
+
if (event.detail === 0 && n === current) return;
|
|
1919
|
+
setValue(value === n ? null : n);
|
|
1920
|
+
},
|
|
1646
1921
|
children: n
|
|
1647
1922
|
},
|
|
1648
1923
|
n
|
|
1649
1924
|
))
|
|
1650
1925
|
}
|
|
1651
1926
|
),
|
|
1652
|
-
hasLabels && /* @__PURE__ */
|
|
1653
|
-
/* @__PURE__ */
|
|
1654
|
-
/* @__PURE__ */
|
|
1927
|
+
hasLabels && /* @__PURE__ */ jsxs5("div", { className: "fillo-scale-labels", children: [
|
|
1928
|
+
/* @__PURE__ */ jsx5("span", { id: minLabelId, children: scale.minLabel }),
|
|
1929
|
+
/* @__PURE__ */ jsx5("span", { id: maxLabelId, children: scale.maxLabel })
|
|
1655
1930
|
] })
|
|
1656
1931
|
] });
|
|
1657
1932
|
}
|
|
1658
1933
|
function Ranking({ field, value, error, setValue, ids: providedIds }) {
|
|
1659
1934
|
const ids = useFilloFieldIds(field.id, providedIds);
|
|
1935
|
+
const strings = useStrings();
|
|
1936
|
+
const announce = useFilloAnnounce();
|
|
1660
1937
|
const ranking = field;
|
|
1661
1938
|
const answered = Array.isArray(value) ? value : [];
|
|
1662
1939
|
const order = [
|
|
1663
1940
|
...answered.filter((id) => ranking.options.some((o) => o.id === id)),
|
|
1664
1941
|
...ranking.options.map((o) => o.id).filter((id) => !answered.includes(id))
|
|
1665
1942
|
];
|
|
1666
|
-
|
|
1943
|
+
const pressedRef = useRef6(null);
|
|
1944
|
+
useEffect6(() => {
|
|
1945
|
+
const pressed = pressedRef.current;
|
|
1946
|
+
pressedRef.current = null;
|
|
1947
|
+
if (!pressed || !pressed.disabled) return;
|
|
1948
|
+
const sibling = pressed.parentElement?.querySelector(
|
|
1949
|
+
".fillo-ranking-move:not(:disabled)"
|
|
1950
|
+
);
|
|
1951
|
+
sibling?.focus();
|
|
1952
|
+
}, [value]);
|
|
1953
|
+
function move(id, delta, button) {
|
|
1667
1954
|
const index = order.indexOf(id);
|
|
1668
1955
|
const target = index + delta;
|
|
1669
1956
|
if (target < 0 || target >= order.length) return;
|
|
1957
|
+
pressedRef.current = button;
|
|
1670
1958
|
const next = [...order];
|
|
1671
1959
|
[next[index], next[target]] = [next[target], next[index]];
|
|
1672
1960
|
setValue(next);
|
|
1961
|
+
const moved = ranking.options.find((o) => o.id === id);
|
|
1962
|
+
if (moved) announce(strings.rankingPosition(moved.label, target + 1, order.length));
|
|
1673
1963
|
}
|
|
1674
|
-
return /* @__PURE__ */
|
|
1964
|
+
return /* @__PURE__ */ jsx5(FieldShell, { field, error, ids, children: /* @__PURE__ */ jsx5(
|
|
1675
1965
|
"div",
|
|
1676
1966
|
{
|
|
1677
1967
|
id: ids.inputId,
|
|
1678
1968
|
role: "group",
|
|
1679
1969
|
"aria-labelledby": ids.labelId,
|
|
1680
1970
|
...fieldAria(field, error, ids),
|
|
1681
|
-
children: /* @__PURE__ */
|
|
1971
|
+
children: /* @__PURE__ */ jsx5("ol", { className: "fillo-ranking", children: order.map((id, index) => {
|
|
1682
1972
|
const option = ranking.options.find((o) => o.id === id);
|
|
1683
1973
|
if (!option) return null;
|
|
1684
|
-
return /* @__PURE__ */
|
|
1685
|
-
/* @__PURE__ */
|
|
1686
|
-
/* @__PURE__ */
|
|
1687
|
-
/* @__PURE__ */
|
|
1688
|
-
/* @__PURE__ */
|
|
1974
|
+
return /* @__PURE__ */ jsxs5("li", { className: "fillo-ranking-item", children: [
|
|
1975
|
+
/* @__PURE__ */ jsx5("span", { className: "fillo-ranking-index", children: index + 1 }),
|
|
1976
|
+
/* @__PURE__ */ jsx5("span", { className: "fillo-ranking-label", children: option.label }),
|
|
1977
|
+
/* @__PURE__ */ jsxs5("span", { className: "fillo-ranking-controls", children: [
|
|
1978
|
+
/* @__PURE__ */ jsx5(
|
|
1689
1979
|
"button",
|
|
1690
1980
|
{
|
|
1691
1981
|
type: "button",
|
|
1692
1982
|
className: "fillo-ranking-move",
|
|
1693
1983
|
"aria-label": `Move ${option.label} up`,
|
|
1694
1984
|
disabled: index === 0,
|
|
1695
|
-
onClick: () => move(id, -1),
|
|
1985
|
+
onClick: (e) => move(id, -1, e.currentTarget),
|
|
1696
1986
|
children: "\u2191"
|
|
1697
1987
|
}
|
|
1698
1988
|
),
|
|
1699
|
-
/* @__PURE__ */
|
|
1989
|
+
/* @__PURE__ */ jsx5(
|
|
1700
1990
|
"button",
|
|
1701
1991
|
{
|
|
1702
1992
|
type: "button",
|
|
1703
1993
|
className: "fillo-ranking-move",
|
|
1704
1994
|
"aria-label": `Move ${option.label} down`,
|
|
1705
1995
|
disabled: index === order.length - 1,
|
|
1706
|
-
onClick: () => move(id, 1),
|
|
1996
|
+
onClick: (e) => move(id, 1, e.currentTarget),
|
|
1707
1997
|
children: "\u2193"
|
|
1708
1998
|
}
|
|
1709
1999
|
)
|
|
@@ -1717,7 +2007,7 @@ function Matrix({ field, value, error, setValue, ids: providedIds }) {
|
|
|
1717
2007
|
const ids = useFilloFieldIds(field.id, providedIds);
|
|
1718
2008
|
const matrix = field;
|
|
1719
2009
|
const answers = value && typeof value === "object" && !Array.isArray(value) ? value : {};
|
|
1720
|
-
return /* @__PURE__ */
|
|
2010
|
+
return /* @__PURE__ */ jsx5(FieldShell, { field, error, ids, children: /* @__PURE__ */ jsx5(
|
|
1721
2011
|
"div",
|
|
1722
2012
|
{
|
|
1723
2013
|
id: ids.inputId,
|
|
@@ -1725,12 +2015,12 @@ function Matrix({ field, value, error, setValue, ids: providedIds }) {
|
|
|
1725
2015
|
role: "group",
|
|
1726
2016
|
"aria-labelledby": ids.labelId,
|
|
1727
2017
|
...fieldAria(field, error, ids),
|
|
1728
|
-
children: /* @__PURE__ */
|
|
1729
|
-
/* @__PURE__ */
|
|
1730
|
-
/* @__PURE__ */
|
|
1731
|
-
matrix.columns.map((col) => /* @__PURE__ */
|
|
2018
|
+
children: /* @__PURE__ */ jsxs5("table", { className: "fillo-matrix", children: [
|
|
2019
|
+
/* @__PURE__ */ jsx5("thead", { children: /* @__PURE__ */ jsxs5("tr", { children: [
|
|
2020
|
+
/* @__PURE__ */ jsx5("th", {}),
|
|
2021
|
+
matrix.columns.map((col) => /* @__PURE__ */ jsx5("th", { scope: "col", children: col.label }, col.id))
|
|
1732
2022
|
] }) }),
|
|
1733
|
-
/* @__PURE__ */
|
|
2023
|
+
/* @__PURE__ */ jsx5("tbody", { children: matrix.rows.map((row) => {
|
|
1734
2024
|
const rowLabelId = `${ids.inputId}-${row.id}`;
|
|
1735
2025
|
const answered = answers[row.id] !== void 0;
|
|
1736
2026
|
const rowInvalid = Boolean(error) && field.required && !answered;
|
|
@@ -1741,17 +2031,17 @@ function Matrix({ field, value, error, setValue, ids: providedIds }) {
|
|
|
1741
2031
|
// radiogroup lives on an inner element that aria-owns the radios
|
|
1742
2032
|
// scattered across the cells (they can't be DOM-nested inside a
|
|
1743
2033
|
// single element within a table row).
|
|
1744
|
-
/* @__PURE__ */
|
|
1745
|
-
/* @__PURE__ */
|
|
2034
|
+
/* @__PURE__ */ jsxs5("tr", { children: [
|
|
2035
|
+
/* @__PURE__ */ jsx5("th", { scope: "row", children: /* @__PURE__ */ jsx5(
|
|
1746
2036
|
"div",
|
|
1747
2037
|
{
|
|
1748
2038
|
role: "radiogroup",
|
|
1749
|
-
"aria-labelledby": rowLabelId
|
|
2039
|
+
"aria-labelledby": `${ids.labelId} ${rowLabelId}`,
|
|
1750
2040
|
"aria-owns": matrix.columns.map(radioId).join(" "),
|
|
1751
|
-
children: /* @__PURE__ */
|
|
2041
|
+
children: /* @__PURE__ */ jsx5("span", { id: rowLabelId, children: row.label })
|
|
1752
2042
|
}
|
|
1753
2043
|
) }),
|
|
1754
|
-
matrix.columns.map((col) => /* @__PURE__ */
|
|
2044
|
+
matrix.columns.map((col) => /* @__PURE__ */ jsx5("td", { "data-label": col.label, children: /* @__PURE__ */ jsx5("label", { className: "fillo-matrix-cell", children: /* @__PURE__ */ jsx5(
|
|
1755
2045
|
"input",
|
|
1756
2046
|
{
|
|
1757
2047
|
id: radioId(col),
|
|
@@ -1764,7 +2054,7 @@ function Matrix({ field, value, error, setValue, ids: providedIds }) {
|
|
|
1764
2054
|
checked: answers[row.id] === col.id,
|
|
1765
2055
|
onChange: () => setValue({ ...answers, [row.id]: col.id })
|
|
1766
2056
|
}
|
|
1767
|
-
) }, col.id))
|
|
2057
|
+
) }) }, col.id))
|
|
1768
2058
|
] }, row.id)
|
|
1769
2059
|
);
|
|
1770
2060
|
}) })
|
|
@@ -1775,12 +2065,45 @@ function Matrix({ field, value, error, setValue, ids: providedIds }) {
|
|
|
1775
2065
|
function Hidden() {
|
|
1776
2066
|
return null;
|
|
1777
2067
|
}
|
|
2068
|
+
function Calculated({ field, value, ids: providedIds }) {
|
|
2069
|
+
const ids = useFilloFieldIds(field.id, providedIds);
|
|
2070
|
+
const state = { kind: field.kind, fieldId: field.id, invalid: false, required: false };
|
|
2071
|
+
const rootCls = useSlotClass("fillo-field fillo-field--calculated fillo-calculated", {
|
|
2072
|
+
slot: "calculated",
|
|
2073
|
+
...state
|
|
2074
|
+
});
|
|
2075
|
+
const labelCls = useSlotClass("fillo-label", { slot: "label", ...state });
|
|
2076
|
+
const descriptionCls = useSlotClass("fillo-description", { slot: "fieldDescription", ...state });
|
|
2077
|
+
const answered = value !== void 0 && value !== null && value !== "";
|
|
2078
|
+
return /* @__PURE__ */ jsxs5(
|
|
2079
|
+
"div",
|
|
2080
|
+
{
|
|
2081
|
+
className: rootCls,
|
|
2082
|
+
"data-fillo": "calculated",
|
|
2083
|
+
"data-field": field.id,
|
|
2084
|
+
"data-kind": field.kind,
|
|
2085
|
+
children: [
|
|
2086
|
+
/* @__PURE__ */ jsx5("label", { className: labelCls, "data-fillo": "label", id: ids.labelId, htmlFor: ids.inputId, children: field.label }),
|
|
2087
|
+
field.description && /* @__PURE__ */ jsx5("p", { className: descriptionCls, "data-fillo": "fieldDescription", id: ids.descriptionId, children: field.description }),
|
|
2088
|
+
/* @__PURE__ */ jsx5(
|
|
2089
|
+
"output",
|
|
2090
|
+
{
|
|
2091
|
+
id: ids.inputId,
|
|
2092
|
+
className: `fillo-calculated-value${answered ? "" : " fillo-calculated-value--empty"}`,
|
|
2093
|
+
"aria-describedby": field.description ? ids.descriptionId : void 0,
|
|
2094
|
+
children: answered ? formatAnswer(field, value) : "\u2014"
|
|
2095
|
+
}
|
|
2096
|
+
)
|
|
2097
|
+
]
|
|
2098
|
+
}
|
|
2099
|
+
);
|
|
2100
|
+
}
|
|
1778
2101
|
var DEFAULT_COMPONENTS = {
|
|
1779
2102
|
short_text: TextInput,
|
|
1780
2103
|
email: TextInput,
|
|
1781
2104
|
url: TextInput,
|
|
1782
2105
|
phone: PhoneField,
|
|
1783
|
-
number:
|
|
2106
|
+
number: NumberInput,
|
|
1784
2107
|
date: TextInput,
|
|
1785
2108
|
long_text: LongText,
|
|
1786
2109
|
select: SingleChoice,
|
|
@@ -1793,16 +2116,17 @@ var DEFAULT_COMPONENTS = {
|
|
|
1793
2116
|
matrix: Matrix,
|
|
1794
2117
|
signature: SignatureField,
|
|
1795
2118
|
file_upload: FileUploadField,
|
|
1796
|
-
hidden: Hidden
|
|
2119
|
+
hidden: Hidden,
|
|
2120
|
+
calculated: Calculated
|
|
1797
2121
|
};
|
|
1798
2122
|
function ContentRenderer({ block }) {
|
|
1799
2123
|
switch (block.kind) {
|
|
1800
2124
|
case "heading":
|
|
1801
|
-
return /* @__PURE__ */
|
|
2125
|
+
return /* @__PURE__ */ jsx5("h3", { className: "fillo-heading", children: block.text });
|
|
1802
2126
|
case "paragraph":
|
|
1803
|
-
return /* @__PURE__ */
|
|
2127
|
+
return /* @__PURE__ */ jsx5("p", { className: "fillo-paragraph", children: block.text });
|
|
1804
2128
|
case "divider":
|
|
1805
|
-
return /* @__PURE__ */
|
|
2129
|
+
return /* @__PURE__ */ jsx5("hr", { className: "fillo-divider" });
|
|
1806
2130
|
}
|
|
1807
2131
|
}
|
|
1808
2132
|
function blockHasPiping(block) {
|
|
@@ -1841,7 +2165,7 @@ function BlockRendererInner({ block, api, components, customComponents }) {
|
|
|
1841
2165
|
block = pipeBlock(block, api.data, api.form);
|
|
1842
2166
|
const ids = useFilloFieldIds(block.id);
|
|
1843
2167
|
const strings = useStrings();
|
|
1844
|
-
if (!isField(block)) return /* @__PURE__ */
|
|
2168
|
+
if (!isField(block)) return /* @__PURE__ */ jsx5(ContentRenderer, { block });
|
|
1845
2169
|
let Component;
|
|
1846
2170
|
if (block.kind === "custom") {
|
|
1847
2171
|
Component = customComponents?.[block.component] ?? components?.custom;
|
|
@@ -1859,7 +2183,7 @@ function BlockRendererInner({ block, api, components, customComponents }) {
|
|
|
1859
2183
|
}
|
|
1860
2184
|
const rawError = api.errors[block.id];
|
|
1861
2185
|
const error = rawError === REQUIRED_FIELD_MESSAGE ? strings.required : rawError;
|
|
1862
|
-
return /* @__PURE__ */
|
|
2186
|
+
return /* @__PURE__ */ jsx5(
|
|
1863
2187
|
Component,
|
|
1864
2188
|
{
|
|
1865
2189
|
field: block,
|
|
@@ -1896,7 +2220,7 @@ function FormField({
|
|
|
1896
2220
|
const block = api.form.pages.flatMap((p) => p.blocks).find((b) => b.id === id);
|
|
1897
2221
|
if (!block) return null;
|
|
1898
2222
|
if (isField(block) && !visibleIds.has(block.id)) return null;
|
|
1899
|
-
return /* @__PURE__ */
|
|
2223
|
+
return /* @__PURE__ */ jsx5(
|
|
1900
2224
|
BlockRenderer,
|
|
1901
2225
|
{
|
|
1902
2226
|
block,
|
|
@@ -1908,8 +2232,8 @@ function FormField({
|
|
|
1908
2232
|
}
|
|
1909
2233
|
|
|
1910
2234
|
// src/turnstile.tsx
|
|
1911
|
-
import { useEffect as useEffect7, useRef as
|
|
1912
|
-
import { jsx as
|
|
2235
|
+
import { useEffect as useEffect7, useRef as useRef7 } from "react";
|
|
2236
|
+
import { jsx as jsx6 } from "react/jsx-runtime";
|
|
1913
2237
|
var SCRIPT_SRC = "https://challenges.cloudflare.com/turnstile/v0/api.js?render=explicit";
|
|
1914
2238
|
var SCRIPT_ID = "fillo-turnstile-script";
|
|
1915
2239
|
var scriptPromise = null;
|
|
@@ -1967,13 +2291,13 @@ function TurnstileWidget({
|
|
|
1967
2291
|
registerReset,
|
|
1968
2292
|
className
|
|
1969
2293
|
}) {
|
|
1970
|
-
const containerRef =
|
|
1971
|
-
const widgetIdRef =
|
|
1972
|
-
const onTokenRef =
|
|
2294
|
+
const containerRef = useRef7(null);
|
|
2295
|
+
const widgetIdRef = useRef7(null);
|
|
2296
|
+
const onTokenRef = useRef7(onToken);
|
|
1973
2297
|
onTokenRef.current = onToken;
|
|
1974
|
-
const onErrorRef =
|
|
2298
|
+
const onErrorRef = useRef7(onError);
|
|
1975
2299
|
onErrorRef.current = onError;
|
|
1976
|
-
const registerResetRef =
|
|
2300
|
+
const registerResetRef = useRef7(registerReset);
|
|
1977
2301
|
registerResetRef.current = registerReset;
|
|
1978
2302
|
useEffect7(() => {
|
|
1979
2303
|
let cancelled = false;
|
|
@@ -2016,7 +2340,7 @@ function TurnstileWidget({
|
|
|
2016
2340
|
onTokenRef.current(void 0);
|
|
2017
2341
|
};
|
|
2018
2342
|
}, [siteKey]);
|
|
2019
|
-
return /* @__PURE__ */
|
|
2343
|
+
return /* @__PURE__ */ jsx6(
|
|
2020
2344
|
"div",
|
|
2021
2345
|
{
|
|
2022
2346
|
ref: containerRef,
|
|
@@ -2027,9 +2351,9 @@ function TurnstileWidget({
|
|
|
2027
2351
|
}
|
|
2028
2352
|
|
|
2029
2353
|
// src/FilloForm.tsx
|
|
2030
|
-
import { Fragment as
|
|
2354
|
+
import { Fragment as Fragment3, jsx as jsx7, jsxs as jsxs6 } from "react/jsx-runtime";
|
|
2031
2355
|
function themeProps(theme) {
|
|
2032
|
-
const safeTheme = normalizeFormTheme(theme);
|
|
2356
|
+
const safeTheme = resolveThemeAppearance(normalizeFormTheme(theme));
|
|
2033
2357
|
if (!safeTheme) return {};
|
|
2034
2358
|
const style = {};
|
|
2035
2359
|
if (safeTheme.primary) style["--fillo-primary"] = safeTheme.primary;
|
|
@@ -2044,32 +2368,30 @@ function themeProps(theme) {
|
|
|
2044
2368
|
...hasColorTheme ? { "data-fillo-has-color-theme": "" } : {}
|
|
2045
2369
|
};
|
|
2046
2370
|
}
|
|
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
2371
|
function badgeUrl(href) {
|
|
2058
2372
|
const url = new URL(safeHttpUrl(href) ?? "https://fillo.so");
|
|
2059
2373
|
url.searchParams.set("utm_source", "form_badge");
|
|
2060
2374
|
return url.toString();
|
|
2061
2375
|
}
|
|
2062
|
-
|
|
2063
|
-
|
|
2064
|
-
|
|
2065
|
-
|
|
2066
|
-
|
|
2067
|
-
|
|
2068
|
-
target?.focus?.();
|
|
2069
|
-
};
|
|
2376
|
+
var FOCUSABLE_SELECTOR = 'input:not([disabled]), textarea:not([disabled]), select:not([disabled]), button:not([disabled]), [tabindex]:not([tabindex="-1"])';
|
|
2377
|
+
function resolveFocusable(el) {
|
|
2378
|
+
if (!el) return null;
|
|
2379
|
+
return el.matches(FOCUSABLE_SELECTOR) ? el : el.querySelector(FOCUSABLE_SELECTOR);
|
|
2380
|
+
}
|
|
2381
|
+
function nextFrame(run) {
|
|
2070
2382
|
if (typeof requestAnimationFrame === "function") requestAnimationFrame(run);
|
|
2071
2383
|
else setTimeout(run, 0);
|
|
2072
2384
|
}
|
|
2385
|
+
function focusErrorSummary(formEl) {
|
|
2386
|
+
if (!formEl || typeof window === "undefined") return;
|
|
2387
|
+
nextFrame(() => {
|
|
2388
|
+
formEl.querySelector(".fillo-error-summary")?.focus();
|
|
2389
|
+
});
|
|
2390
|
+
}
|
|
2391
|
+
function focusField(inputId) {
|
|
2392
|
+
if (typeof document === "undefined") return;
|
|
2393
|
+
resolveFocusable(document.getElementById(inputId))?.focus();
|
|
2394
|
+
}
|
|
2073
2395
|
var HP_STYLE = {
|
|
2074
2396
|
position: "absolute",
|
|
2075
2397
|
left: "-9999px",
|
|
@@ -2094,6 +2416,9 @@ function FilloForm(props) {
|
|
|
2094
2416
|
);
|
|
2095
2417
|
const [loaded, setLoaded] = useState6(null);
|
|
2096
2418
|
const [loadFailure, setLoadFailure] = useState6(null);
|
|
2419
|
+
const devEnv = useIsDevEnv();
|
|
2420
|
+
const devChrome = props.preview === true || devEnv;
|
|
2421
|
+
if (props.preview === true) warnPreviewInProduction();
|
|
2097
2422
|
const codeForm = isCodeForm2(form) ? form : null;
|
|
2098
2423
|
const onError = props.onError;
|
|
2099
2424
|
const {
|
|
@@ -2105,16 +2430,22 @@ function FilloForm(props) {
|
|
|
2105
2430
|
noticeError: syncNoticeError,
|
|
2106
2431
|
syncing: codeFormSyncing,
|
|
2107
2432
|
staged: codeChangesStaged,
|
|
2433
|
+
accepting: syncedAccepting,
|
|
2434
|
+
acceptingReason: syncedAcceptingReason,
|
|
2435
|
+
uploadsAvailable: syncedUploadsAvailable,
|
|
2436
|
+
uploadFileSizeLimitMb: syncedUploadFileSizeLimitMb,
|
|
2108
2437
|
resolvedSchema,
|
|
2109
|
-
resolvedTheme
|
|
2438
|
+
resolvedTheme,
|
|
2439
|
+
warningCode: syncedWarningCode,
|
|
2440
|
+
warningUrl: syncedWarningUrl
|
|
2110
2441
|
} = useCodeFormSync(form, client, onError, {
|
|
2111
2442
|
// An absent client is intentional render-only use. Builder previews and an
|
|
2112
2443
|
// explicit canonical id also do not need publishable-key resolution.
|
|
2113
2444
|
allowUnsynced: Boolean(!client || props.skipValidation || formId)
|
|
2114
2445
|
});
|
|
2115
|
-
const renderResolvedSnapshot = Boolean(resolvedSchema && !
|
|
2446
|
+
const renderResolvedSnapshot = Boolean(resolvedSchema && !devChrome);
|
|
2116
2447
|
const inlineSchema = codeForm ? renderResolvedSnapshot ? resolvedSchema : codeForm.schema : form;
|
|
2117
|
-
const onErrorRef =
|
|
2448
|
+
const onErrorRef = useRef8(onError);
|
|
2118
2449
|
useEffect8(() => {
|
|
2119
2450
|
onErrorRef.current = onError;
|
|
2120
2451
|
});
|
|
@@ -2135,6 +2466,10 @@ function FilloForm(props) {
|
|
|
2135
2466
|
schema: published.schema,
|
|
2136
2467
|
theme: published.theme,
|
|
2137
2468
|
closed: Boolean(published.closed),
|
|
2469
|
+
accepting: published.accepting,
|
|
2470
|
+
acceptingReason: published.acceptingReason,
|
|
2471
|
+
uploadsAvailable: published.uploadsAvailable,
|
|
2472
|
+
uploadFileSizeLimitMb: published.uploadFileSizeLimitMb,
|
|
2138
2473
|
branding: published.branding ?? null,
|
|
2139
2474
|
challenge: published.challenge ?? null
|
|
2140
2475
|
});
|
|
@@ -2158,25 +2493,52 @@ function FilloForm(props) {
|
|
|
2158
2493
|
const theme = props.appearance?.theme ?? props.theme ?? resolvedCodeTheme ?? currentLoaded?.theme ?? void 0;
|
|
2159
2494
|
if (normalized && !normalized.ok) {
|
|
2160
2495
|
const error = new FilloError2(`This form could not be rendered: ${normalized.error}`, 422);
|
|
2161
|
-
if (props.renderError) return /* @__PURE__ */
|
|
2162
|
-
return /* @__PURE__ */
|
|
2496
|
+
if (props.renderError) return /* @__PURE__ */ jsx7(Fragment3, { children: props.renderError(error) });
|
|
2497
|
+
return /* @__PURE__ */ jsx7("div", { className: `fillo-form fillo-form--error ${props.className ?? ""}`, children: strings.renderFailed });
|
|
2163
2498
|
}
|
|
2164
2499
|
if (loadError) {
|
|
2165
2500
|
if (props.renderError) {
|
|
2166
|
-
return /* @__PURE__ */
|
|
2501
|
+
return /* @__PURE__ */ jsx7(Fragment3, { children: props.renderError(loadError) });
|
|
2167
2502
|
}
|
|
2168
2503
|
const message = loadError.status === 404 ? strings.loadFailedNotFound : loadError.status === 0 ? strings.loadFailedNetwork : strings.loadFailed;
|
|
2169
|
-
return /* @__PURE__ */
|
|
2504
|
+
return /* @__PURE__ */ jsx7("div", { className: `fillo-form fillo-form--error ${props.className ?? ""}`, children: message });
|
|
2505
|
+
}
|
|
2506
|
+
if (currentLoaded?.accepting === false && schema) {
|
|
2507
|
+
const variant = overlayVariant(currentLoaded.acceptingReason);
|
|
2508
|
+
if (props.renderError) {
|
|
2509
|
+
const error = variant === "closed" ? new FilloError2(
|
|
2510
|
+
"This form is no longer accepting responses.",
|
|
2511
|
+
403,
|
|
2512
|
+
void 0,
|
|
2513
|
+
currentLoaded.acceptingReason
|
|
2514
|
+
) : new FilloError2(
|
|
2515
|
+
"This form isn't accepting responses yet.",
|
|
2516
|
+
403,
|
|
2517
|
+
void 0,
|
|
2518
|
+
currentLoaded.acceptingReason
|
|
2519
|
+
);
|
|
2520
|
+
return /* @__PURE__ */ jsx7(Fragment3, { children: props.renderError(error) });
|
|
2521
|
+
}
|
|
2522
|
+
return /* @__PURE__ */ jsx7(
|
|
2523
|
+
NotOpenOverlay,
|
|
2524
|
+
{
|
|
2525
|
+
...props,
|
|
2526
|
+
variant,
|
|
2527
|
+
schema,
|
|
2528
|
+
theme,
|
|
2529
|
+
branding: currentLoaded.branding
|
|
2530
|
+
}
|
|
2531
|
+
);
|
|
2170
2532
|
}
|
|
2171
2533
|
if (currentLoaded?.closed) {
|
|
2172
|
-
return /* @__PURE__ */
|
|
2534
|
+
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
2535
|
}
|
|
2174
2536
|
const canonicalSyncError = codeForm && syncError && !props.skipValidation ? syncError : null;
|
|
2175
2537
|
if (canonicalSyncError && props.renderError) {
|
|
2176
|
-
return /* @__PURE__ */
|
|
2538
|
+
return /* @__PURE__ */ jsx7(Fragment3, { children: props.renderError(canonicalSyncError) });
|
|
2177
2539
|
}
|
|
2178
|
-
if (canonicalSyncError && !
|
|
2179
|
-
return /* @__PURE__ */
|
|
2540
|
+
if (canonicalSyncError && !devChrome) {
|
|
2541
|
+
return /* @__PURE__ */ jsx7(
|
|
2180
2542
|
"div",
|
|
2181
2543
|
{
|
|
2182
2544
|
className: `fillo-form fillo-form--error ${props.className ?? ""}`,
|
|
@@ -2189,23 +2551,42 @@ function FilloForm(props) {
|
|
|
2189
2551
|
);
|
|
2190
2552
|
}
|
|
2191
2553
|
const notPublished = Boolean(codeForm) && syncedStatus === "draft" && !props.skipValidation;
|
|
2192
|
-
|
|
2193
|
-
|
|
2194
|
-
|
|
2195
|
-
|
|
2554
|
+
const syncNotAccepting = Boolean(codeForm) && !props.skipValidation && syncedStatus === "published" && syncedAccepting === false;
|
|
2555
|
+
if ((notPublished || syncNotAccepting) && !devChrome) {
|
|
2556
|
+
const variant = notPublished ? "notOpen" : overlayVariant(syncedAcceptingReason);
|
|
2557
|
+
const error = variant === "closed" ? new FilloError2(
|
|
2558
|
+
"This form is no longer accepting responses.",
|
|
2559
|
+
403,
|
|
2560
|
+
void 0,
|
|
2561
|
+
syncedAcceptingReason
|
|
2562
|
+
) : new FilloError2("This form isn't published yet.", 403);
|
|
2563
|
+
if (props.renderError) return /* @__PURE__ */ jsx7(Fragment3, { children: props.renderError(error) });
|
|
2564
|
+
if (schema) {
|
|
2565
|
+
return /* @__PURE__ */ jsx7(
|
|
2566
|
+
NotOpenOverlay,
|
|
2567
|
+
{
|
|
2568
|
+
...props,
|
|
2569
|
+
variant,
|
|
2570
|
+
schema,
|
|
2571
|
+
theme,
|
|
2572
|
+
branding: syncedBranding
|
|
2573
|
+
}
|
|
2574
|
+
);
|
|
2575
|
+
}
|
|
2576
|
+
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
2577
|
}
|
|
2197
2578
|
const awaitingCanonical = Boolean(codeForm && codeFormSyncing && !props.skipValidation);
|
|
2198
|
-
if (!
|
|
2199
|
-
return /* @__PURE__ */
|
|
2579
|
+
if (!devChrome && awaitingCanonical || !schema) {
|
|
2580
|
+
return /* @__PURE__ */ jsxs6(
|
|
2200
2581
|
"div",
|
|
2201
2582
|
{
|
|
2202
2583
|
className: `fillo-form fillo-form--loading ${props.className ?? ""}`,
|
|
2203
2584
|
"aria-busy": "true",
|
|
2204
2585
|
...themeProps(theme),
|
|
2205
2586
|
children: [
|
|
2206
|
-
/* @__PURE__ */
|
|
2207
|
-
/* @__PURE__ */
|
|
2208
|
-
/* @__PURE__ */
|
|
2587
|
+
/* @__PURE__ */ jsx7("div", { className: "fillo-skeleton" }),
|
|
2588
|
+
/* @__PURE__ */ jsx7("div", { className: "fillo-skeleton" }),
|
|
2589
|
+
/* @__PURE__ */ jsx7("div", { className: "fillo-skeleton fillo-skeleton--short" })
|
|
2209
2590
|
]
|
|
2210
2591
|
}
|
|
2211
2592
|
);
|
|
@@ -2213,7 +2594,7 @@ function FilloForm(props) {
|
|
|
2213
2594
|
const branding = codeForm ? syncedBranding : currentLoaded?.branding;
|
|
2214
2595
|
const poweredBy = branding?.poweredBy ?? true;
|
|
2215
2596
|
const challenge = props.challenge ?? (codeForm ? syncedChallenge : currentLoaded?.challenge) ?? void 0;
|
|
2216
|
-
return /* @__PURE__ */
|
|
2597
|
+
return /* @__PURE__ */ jsx7(
|
|
2217
2598
|
ResolvedForm,
|
|
2218
2599
|
{
|
|
2219
2600
|
...props,
|
|
@@ -2227,60 +2608,84 @@ function FilloForm(props) {
|
|
|
2227
2608
|
draftNotice: notPublished,
|
|
2228
2609
|
stagedNotice: codeChangesStaged,
|
|
2229
2610
|
syncErrorNotice: syncError ?? syncNoticeError ?? void 0,
|
|
2611
|
+
uploadsAvailable: codeForm ? syncedUploadsAvailable : currentLoaded?.uploadsAvailable,
|
|
2612
|
+
uploadFileSizeLimitMb: codeForm ? syncedUploadFileSizeLimitMb : currentLoaded?.uploadFileSizeLimitMb ?? props.uploadFileSizeLimitMb,
|
|
2613
|
+
syncWarningCode: codeForm ? syncedWarningCode : void 0,
|
|
2614
|
+
syncWarningUrl: syncedWarningUrl,
|
|
2230
2615
|
challenge
|
|
2231
2616
|
}
|
|
2232
2617
|
);
|
|
2233
2618
|
}
|
|
2234
2619
|
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
|
-
] });
|
|
2620
|
+
return /* @__PURE__ */ jsx7("a", { className: "fillo-powered", href: badgeUrl(href), target: "_blank", rel: "noopener noreferrer", children: label ?? "Powered by Fillo" });
|
|
2247
2621
|
}
|
|
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
|
-
] });
|
|
2622
|
+
function overlayVariant(reason) {
|
|
2623
|
+
return reason === "expired" || reason === "capped" || reason === "storage_full" ? "closed" : "notOpen";
|
|
2253
2624
|
}
|
|
2254
|
-
function
|
|
2255
|
-
|
|
2256
|
-
|
|
2257
|
-
|
|
2258
|
-
|
|
2625
|
+
function NotOpenOverlay(props) {
|
|
2626
|
+
const strings = resolveStrings(props.strings);
|
|
2627
|
+
const closedFlavor = props.variant === "closed";
|
|
2628
|
+
return /* @__PURE__ */ jsxs6(
|
|
2629
|
+
"div",
|
|
2630
|
+
{
|
|
2631
|
+
className: `fillo-form fillo-form--not-open ${props.className ?? ""}`,
|
|
2632
|
+
"data-fillo": "root",
|
|
2633
|
+
"data-state": "closed",
|
|
2634
|
+
...themeProps(props.theme),
|
|
2635
|
+
children: [
|
|
2636
|
+
/* @__PURE__ */ jsx7(
|
|
2637
|
+
"div",
|
|
2638
|
+
{
|
|
2639
|
+
className: "fillo-not-open-preview",
|
|
2640
|
+
"aria-hidden": "true",
|
|
2641
|
+
ref: (node) => node?.setAttribute("inert", ""),
|
|
2642
|
+
children: /* @__PURE__ */ jsx7(
|
|
2643
|
+
ResolvedForm,
|
|
2644
|
+
{
|
|
2645
|
+
schema: props.schema,
|
|
2646
|
+
theme: props.theme,
|
|
2647
|
+
appearance: props.appearance,
|
|
2648
|
+
strings: props.strings,
|
|
2649
|
+
components: props.components,
|
|
2650
|
+
customComponents: props.customComponents,
|
|
2651
|
+
initialData: props.initialData,
|
|
2652
|
+
showTitle: props.showTitle,
|
|
2653
|
+
displayOnly: true
|
|
2654
|
+
}
|
|
2655
|
+
)
|
|
2656
|
+
}
|
|
2657
|
+
),
|
|
2658
|
+
/* @__PURE__ */ jsxs6("div", { className: "fillo-not-open-card", "data-fillo": "not-open-card", role: "status", children: [
|
|
2659
|
+
/* @__PURE__ */ jsx7("h2", { className: "fillo-not-open-title", children: closedFlavor ? strings.closedTitle : strings.notOpenTitle }),
|
|
2660
|
+
/* @__PURE__ */ jsx7("p", { className: "fillo-not-open-body", children: closedFlavor ? strings.closed : strings.notOpenBody })
|
|
2661
|
+
] }),
|
|
2662
|
+
(props.branding?.poweredBy ?? true) && /* @__PURE__ */ jsx7(PoweredBy, { label: props.branding?.label, href: props.branding?.href })
|
|
2663
|
+
]
|
|
2664
|
+
}
|
|
2665
|
+
);
|
|
2259
2666
|
}
|
|
2260
|
-
|
|
2261
|
-
|
|
2262
|
-
|
|
2263
|
-
|
|
2264
|
-
error.code ? ` (${error.code})` : "",
|
|
2265
|
-
"."
|
|
2266
|
-
] }),
|
|
2267
|
-
" ",
|
|
2268
|
-
error.message,
|
|
2269
|
-
" The safe form remains available for development."
|
|
2270
|
-
] });
|
|
2667
|
+
var noopSubscribe = () => () => {
|
|
2668
|
+
};
|
|
2669
|
+
function useIsDevEnv() {
|
|
2670
|
+
return useSyncExternalStore2(noopSubscribe, isLikelyDevEnv, isBuildTimeDevEnv2);
|
|
2271
2671
|
}
|
|
2272
2672
|
function ResolvedForm(props) {
|
|
2273
2673
|
const { schema, theme } = props;
|
|
2674
|
+
const displayOnly = props.displayOnly === true;
|
|
2274
2675
|
const instanceId = useId2().replace(/:/g, "");
|
|
2275
|
-
const
|
|
2676
|
+
const devEnv = useIsDevEnv();
|
|
2677
|
+
const devChrome = !displayOnly && (props.preview === true || devEnv);
|
|
2678
|
+
const hpRef = useRef8(null);
|
|
2276
2679
|
const codeForm = isCodeForm2(props.form) ? props.form : null;
|
|
2277
2680
|
const client = props.client;
|
|
2278
2681
|
const challenge = props.challenge;
|
|
2279
2682
|
const challengeRequired = Boolean(challenge);
|
|
2280
|
-
const challengeTokenRef =
|
|
2683
|
+
const challengeTokenRef = useRef8(void 0);
|
|
2684
|
+
const [resyncWarning, setResyncWarning] = useState6(null);
|
|
2685
|
+
const [resolutionFailureCode, setResolutionFailureCode] = useState6(null);
|
|
2281
2686
|
const [challengeToken, setChallengeToken] = useState6(void 0);
|
|
2282
2687
|
const [challengeError, setChallengeError] = useState6(false);
|
|
2283
|
-
const resetChallengeRef =
|
|
2688
|
+
const resetChallengeRef = useRef8(void 0);
|
|
2284
2689
|
const handleChallengeToken = useCallback((token) => {
|
|
2285
2690
|
challengeTokenRef.current = token;
|
|
2286
2691
|
setChallengeToken(token);
|
|
@@ -2295,6 +2700,12 @@ function ResolvedForm(props) {
|
|
|
2295
2700
|
async () => {
|
|
2296
2701
|
try {
|
|
2297
2702
|
const result = await syncCodeForm2(client, codeForm, { bypassCache: true });
|
|
2703
|
+
setResyncWarning({
|
|
2704
|
+
code: result.warningCode,
|
|
2705
|
+
url: result.warningUrl,
|
|
2706
|
+
uploadsAvailable: result.uploadsAvailable,
|
|
2707
|
+
uploadFileSizeLimitMb: result.uploadFileSizeLimitMb
|
|
2708
|
+
});
|
|
2298
2709
|
if (result.status === "draft") {
|
|
2299
2710
|
throw new FilloError2(
|
|
2300
2711
|
"This form is no longer published. Submission was stopped before sending answers.",
|
|
@@ -2303,6 +2714,14 @@ function ResolvedForm(props) {
|
|
|
2303
2714
|
"form_not_published"
|
|
2304
2715
|
);
|
|
2305
2716
|
}
|
|
2717
|
+
if (result.accepting === false) {
|
|
2718
|
+
throw new FilloError2(
|
|
2719
|
+
"This form is no longer accepting responses. Submission was stopped before sending answers.",
|
|
2720
|
+
403,
|
|
2721
|
+
void 0,
|
|
2722
|
+
result.acceptingReason ?? "form_not_accepting"
|
|
2723
|
+
);
|
|
2724
|
+
}
|
|
2306
2725
|
const authoritative = normalizeFormSchema(result.resolvedSchema ?? codeForm.schema);
|
|
2307
2726
|
if (!authoritative.ok) {
|
|
2308
2727
|
throw new FilloError2(
|
|
@@ -2332,9 +2751,11 @@ function ResolvedForm(props) {
|
|
|
2332
2751
|
);
|
|
2333
2752
|
props.onError?.(notice);
|
|
2334
2753
|
}
|
|
2754
|
+
setResolutionFailureCode(null);
|
|
2335
2755
|
return result.formId;
|
|
2336
2756
|
} catch (error) {
|
|
2337
2757
|
const failure = isFilloError2(error) ? error : new FilloError2(error instanceof Error ? error.message : String(error), 0);
|
|
2758
|
+
setResolutionFailureCode(failure.code ?? "sync_failed");
|
|
2338
2759
|
console.warn(
|
|
2339
2760
|
`[fillo] form sync failed${failure.code ? ` (${failure.code})` : ""}: ${failure.message}`
|
|
2340
2761
|
);
|
|
@@ -2358,6 +2779,9 @@ function ResolvedForm(props) {
|
|
|
2358
2779
|
// "default", never the bare-engine "headless".
|
|
2359
2780
|
surface: "default",
|
|
2360
2781
|
resolveFormId,
|
|
2782
|
+
// Dev chrome gets the developer-grade resolution failure (message + code)
|
|
2783
|
+
// instead of the respondent-safe "unavailable" fallback.
|
|
2784
|
+
verboseResolutionErrors: devChrome,
|
|
2361
2785
|
respondent: props.respondent,
|
|
2362
2786
|
challengeRequired,
|
|
2363
2787
|
getChallengeToken: () => challengeTokenRef.current,
|
|
@@ -2366,8 +2790,44 @@ function ResolvedForm(props) {
|
|
|
2366
2790
|
resetChallengeRef.current?.();
|
|
2367
2791
|
}
|
|
2368
2792
|
});
|
|
2369
|
-
useDraftFlush(api);
|
|
2793
|
+
useDraftFlush(api, !displayOnly);
|
|
2794
|
+
const strings = resolveStrings(props.strings);
|
|
2795
|
+
const announceRef = useRef8(null);
|
|
2796
|
+
const announce = useCallback((text) => {
|
|
2797
|
+
const el = announceRef.current;
|
|
2798
|
+
if (!el) return;
|
|
2799
|
+
if (el.textContent === text) {
|
|
2800
|
+
el.textContent = "";
|
|
2801
|
+
nextFrame(() => {
|
|
2802
|
+
if (announceRef.current) announceRef.current.textContent = text;
|
|
2803
|
+
});
|
|
2804
|
+
return;
|
|
2805
|
+
}
|
|
2806
|
+
el.textContent = text;
|
|
2807
|
+
}, []);
|
|
2808
|
+
useEffect8(() => {
|
|
2809
|
+
if (api.status === "submitting") announce(strings.submittingAnnouncement);
|
|
2810
|
+
}, [api.status, announce, strings.submittingAnnouncement]);
|
|
2811
|
+
const resuming = api.resumedDraft || api.editingPrevious;
|
|
2812
|
+
const resumeAnnouncement = api.editingPrevious ? strings.editNotice : strings.resumeNotice;
|
|
2813
|
+
useEffect8(() => {
|
|
2814
|
+
if (resuming) announce(resumeAnnouncement);
|
|
2815
|
+
}, [resuming, resumeAnnouncement, announce]);
|
|
2370
2816
|
const themed = useMemo5(() => themeProps(theme), [theme]);
|
|
2817
|
+
const warningCode = resyncWarning ? resyncWarning.code : props.syncWarningCode;
|
|
2818
|
+
const warningUrl = resyncWarning ? resyncWarning.url : props.syncWarningUrl;
|
|
2819
|
+
const uploadsAvailable = resyncWarning ? resyncWarning.uploadsAvailable : props.uploadsAvailable;
|
|
2820
|
+
const uploadFileSizeLimitMb = resyncWarning ? resyncWarning.uploadFileSizeLimitMb : props.uploadFileSizeLimitMb;
|
|
2821
|
+
const chromeValue = useMemo5(
|
|
2822
|
+
() => ({
|
|
2823
|
+
devChrome,
|
|
2824
|
+
uploadsAvailable,
|
|
2825
|
+
uploadFileSizeLimitMb,
|
|
2826
|
+
warningCode,
|
|
2827
|
+
warningUrl
|
|
2828
|
+
}),
|
|
2829
|
+
[devChrome, uploadsAvailable, uploadFileSizeLimitMb, warningCode, warningUrl]
|
|
2830
|
+
);
|
|
2371
2831
|
const settings = schema.settings;
|
|
2372
2832
|
const submitted = api.status === "submitted";
|
|
2373
2833
|
const liveSubmit = submitted && !api.restoredSubmission;
|
|
@@ -2376,21 +2836,25 @@ function ResolvedForm(props) {
|
|
|
2376
2836
|
const url = safeHttpUrl(settings.redirectUrl);
|
|
2377
2837
|
if (url) window.location.assign(url);
|
|
2378
2838
|
}, [liveSubmit, settings.redirectUrl]);
|
|
2379
|
-
const successRef =
|
|
2839
|
+
const successRef = useRef8(null);
|
|
2380
2840
|
useEffect8(() => {
|
|
2381
2841
|
if (liveSubmit) successRef.current?.focus();
|
|
2382
2842
|
}, [liveSubmit]);
|
|
2383
|
-
const blocksRef =
|
|
2384
|
-
const prevPageIndex =
|
|
2843
|
+
const blocksRef = useRef8(null);
|
|
2844
|
+
const prevPageIndex = useRef8(api.pageIndex);
|
|
2385
2845
|
useEffect8(() => {
|
|
2386
2846
|
if (prevPageIndex.current === api.pageIndex) return;
|
|
2387
2847
|
prevPageIndex.current = api.pageIndex;
|
|
2388
|
-
blocksRef.current?.
|
|
2848
|
+
const container = blocksRef.current?.parentElement;
|
|
2849
|
+
const heading = container?.querySelector(".fillo-page-title, .fillo-title");
|
|
2850
|
+
const target = heading ?? blocksRef.current;
|
|
2851
|
+
if (!target) return;
|
|
2852
|
+
if (!target.hasAttribute("tabindex")) target.setAttribute("tabindex", "-1");
|
|
2853
|
+
target.focus();
|
|
2389
2854
|
}, [api.pageIndex]);
|
|
2390
2855
|
const appearance = props.appearance;
|
|
2391
|
-
const strings = resolveStrings(props.strings);
|
|
2392
2856
|
if (api.status === "submitted") {
|
|
2393
|
-
return /* @__PURE__ */
|
|
2857
|
+
return /* @__PURE__ */ jsxs6(
|
|
2394
2858
|
"div",
|
|
2395
2859
|
{
|
|
2396
2860
|
ref: successRef,
|
|
@@ -2406,19 +2870,19 @@ function ResolvedForm(props) {
|
|
|
2406
2870
|
"data-state": "submitted",
|
|
2407
2871
|
...themed,
|
|
2408
2872
|
children: [
|
|
2409
|
-
props.renderSuccess ? props.renderSuccess() : /* @__PURE__ */
|
|
2873
|
+
props.renderSuccess ? props.renderSuccess() : /* @__PURE__ */ jsxs6(
|
|
2410
2874
|
"div",
|
|
2411
2875
|
{
|
|
2412
2876
|
className: slotClass3("fillo-success", appearance, { slot: "success", status: "submitted" }),
|
|
2413
2877
|
"data-fillo": "success",
|
|
2414
2878
|
children: [
|
|
2415
|
-
/* @__PURE__ */
|
|
2416
|
-
/* @__PURE__ */
|
|
2417
|
-
/* @__PURE__ */
|
|
2879
|
+
/* @__PURE__ */ jsx7("div", { className: "fillo-success-mark", "aria-hidden": "true" }),
|
|
2880
|
+
/* @__PURE__ */ jsx7("h2", { className: "fillo-success-title", children: settings.successTitle ?? strings.successTitle }),
|
|
2881
|
+
/* @__PURE__ */ jsx7("p", { className: "fillo-success-message", children: api.duplicateSubmission ? strings.alreadyAnswered : settings.successMessage ?? strings.successMessage })
|
|
2418
2882
|
]
|
|
2419
2883
|
}
|
|
2420
2884
|
),
|
|
2421
|
-
props.poweredBy && /* @__PURE__ */
|
|
2885
|
+
props.poweredBy && /* @__PURE__ */ jsx7(PoweredBy, { label: props.brandLabel, href: props.brandHref })
|
|
2422
2886
|
]
|
|
2423
2887
|
}
|
|
2424
2888
|
);
|
|
@@ -2434,13 +2898,200 @@ function ResolvedForm(props) {
|
|
|
2434
2898
|
const progressTotal = Math.max(progressSeq.length, 1);
|
|
2435
2899
|
const progressPos = progressSeq.indexOf(api.page.id);
|
|
2436
2900
|
const progressStep = progressPos >= 0 ? progressPos + 1 : Math.min(api.pageIndex + 1, progressTotal);
|
|
2437
|
-
|
|
2901
|
+
const submitFixUrl = devChrome && resolutionFailureCode === "form_not_published" && warningCode === "storage_required" ? safeHttpUrl(warningUrl) : null;
|
|
2902
|
+
const invalidFields = [];
|
|
2903
|
+
for (const block of api.blocks) {
|
|
2904
|
+
if (!isField2(block)) continue;
|
|
2905
|
+
const raw = api.errors[block.id];
|
|
2906
|
+
if (!raw) continue;
|
|
2907
|
+
invalidFields.push({
|
|
2908
|
+
field: block,
|
|
2909
|
+
error: raw === REQUIRED_FIELD_MESSAGE2 ? strings.required : raw,
|
|
2910
|
+
inputId: createFilloFieldIds(instanceId, block.id).inputId
|
|
2911
|
+
});
|
|
2912
|
+
}
|
|
2913
|
+
const rootClass = slotClass3(`fillo-form ${props.className ?? ""}`, appearance, {
|
|
2914
|
+
slot: "root",
|
|
2915
|
+
status: api.status
|
|
2916
|
+
});
|
|
2917
|
+
const formBody = /* @__PURE__ */ jsxs6(Fragment3, { children: [
|
|
2918
|
+
/* @__PURE__ */ jsx7(
|
|
2919
|
+
"span",
|
|
2920
|
+
{
|
|
2921
|
+
ref: announceRef,
|
|
2922
|
+
className: "fillo-sr-only",
|
|
2923
|
+
role: "status",
|
|
2924
|
+
"aria-live": "polite",
|
|
2925
|
+
"data-fillo": "announce"
|
|
2926
|
+
}
|
|
2927
|
+
),
|
|
2928
|
+
devChrome && /* @__PURE__ */ jsx7(
|
|
2929
|
+
DevChrome,
|
|
2930
|
+
{
|
|
2931
|
+
preview: props.preview,
|
|
2932
|
+
devNotices: props.devNotices,
|
|
2933
|
+
syncError: props.syncErrorNotice,
|
|
2934
|
+
staged: props.stagedNotice,
|
|
2935
|
+
draft: props.draftNotice,
|
|
2936
|
+
warningUrl: props.syncWarningUrl,
|
|
2937
|
+
noClient: !props.client && !props.skipValidation
|
|
2938
|
+
}
|
|
2939
|
+
),
|
|
2940
|
+
resuming && /* @__PURE__ */ jsxs6(
|
|
2941
|
+
"div",
|
|
2942
|
+
{
|
|
2943
|
+
className: slotClass3("fillo-resume", appearance, { slot: "resume" }),
|
|
2944
|
+
"data-fillo": "resume",
|
|
2945
|
+
children: [
|
|
2946
|
+
/* @__PURE__ */ jsx7("span", { className: "fillo-resume-text", children: api.editingPrevious ? strings.editNotice : strings.resumeNotice }),
|
|
2947
|
+
/* @__PURE__ */ jsx7("button", { type: "button", className: "fillo-resume-clear", onClick: api.resetDraft, children: strings.resumeStartOver })
|
|
2948
|
+
]
|
|
2949
|
+
}
|
|
2950
|
+
),
|
|
2951
|
+
api.resumeLinkFailed && !api.resumedDraft && !api.editingPrevious && /* @__PURE__ */ jsx7(
|
|
2952
|
+
"div",
|
|
2953
|
+
{
|
|
2954
|
+
className: slotClass3("fillo-resume fillo-resume--expired", appearance, { slot: "resume" }),
|
|
2955
|
+
"data-fillo": "resume",
|
|
2956
|
+
role: "status",
|
|
2957
|
+
children: /* @__PURE__ */ jsx7("span", { className: "fillo-resume-text", children: strings.resumeLinkExpired })
|
|
2958
|
+
}
|
|
2959
|
+
),
|
|
2960
|
+
multiPage && settings.showProgress !== false && /* @__PURE__ */ jsx7(
|
|
2961
|
+
"div",
|
|
2962
|
+
{
|
|
2963
|
+
className: slotClass3("fillo-progress-track", appearance, { slot: "progress" }),
|
|
2964
|
+
"data-fillo": "progress",
|
|
2965
|
+
role: "progressbar",
|
|
2966
|
+
"aria-label": schema.title || "Form progress",
|
|
2967
|
+
"aria-valuemin": 1,
|
|
2968
|
+
"aria-valuemax": progressTotal,
|
|
2969
|
+
"aria-valuenow": progressStep,
|
|
2970
|
+
children: /* @__PURE__ */ jsx7(
|
|
2971
|
+
"div",
|
|
2972
|
+
{
|
|
2973
|
+
className: slotClass3("fillo-progress-fill", appearance, { slot: "progressFill" }),
|
|
2974
|
+
"data-fillo": "progressFill",
|
|
2975
|
+
style: { "--fillo-progress-value": `${progressStep / progressTotal * 100}%` }
|
|
2976
|
+
}
|
|
2977
|
+
)
|
|
2978
|
+
}
|
|
2979
|
+
),
|
|
2980
|
+
api.pageIndex === 0 && props.showTitle !== false && /* @__PURE__ */ jsxs6("header", { className: slotClass3("fillo-header", appearance, { slot: "header" }), "data-fillo": "header", children: [
|
|
2981
|
+
schema.title && /* @__PURE__ */ jsx7("h1", { className: slotClass3("fillo-title", appearance, { slot: "title" }), "data-fillo": "title", children: schema.title }),
|
|
2982
|
+
schema.description && /* @__PURE__ */ jsx7(
|
|
2983
|
+
"p",
|
|
2984
|
+
{
|
|
2985
|
+
className: slotClass3("fillo-form-description", appearance, { slot: "description" }),
|
|
2986
|
+
"data-fillo": "description",
|
|
2987
|
+
children: schema.description
|
|
2988
|
+
}
|
|
2989
|
+
)
|
|
2990
|
+
] }),
|
|
2991
|
+
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 }),
|
|
2992
|
+
invalidFields.length > 0 && /* @__PURE__ */ jsxs6("div", { className: "fillo-error-summary", role: "alert", tabIndex: -1, "data-fillo": "error-summary", children: [
|
|
2993
|
+
/* @__PURE__ */ jsx7("p", { className: "fillo-error-summary-title", children: strings.errorSummaryTitle }),
|
|
2994
|
+
/* @__PURE__ */ jsx7("ul", { children: invalidFields.map(({ field, error, inputId }) => /* @__PURE__ */ jsx7("li", { children: /* @__PURE__ */ jsxs6(
|
|
2995
|
+
"a",
|
|
2996
|
+
{
|
|
2997
|
+
href: `#${inputId}`,
|
|
2998
|
+
onClick: (e) => {
|
|
2999
|
+
e.preventDefault();
|
|
3000
|
+
focusField(inputId);
|
|
3001
|
+
},
|
|
3002
|
+
children: [
|
|
3003
|
+
field.label,
|
|
3004
|
+
": ",
|
|
3005
|
+
error
|
|
3006
|
+
]
|
|
3007
|
+
}
|
|
3008
|
+
) }, field.id)) })
|
|
3009
|
+
] }),
|
|
3010
|
+
/* @__PURE__ */ jsx7("div", { className: slotClass3("fillo-blocks", appearance, { slot: "blocks" }), "data-fillo": "blocks", ref: blocksRef, tabIndex: -1, children: api.blocks.map((block) => /* @__PURE__ */ jsx7(
|
|
3011
|
+
BlockRenderer,
|
|
3012
|
+
{
|
|
3013
|
+
block,
|
|
3014
|
+
api,
|
|
3015
|
+
components: props.components,
|
|
3016
|
+
customComponents: props.customComponents
|
|
3017
|
+
},
|
|
3018
|
+
block.id
|
|
3019
|
+
)) }),
|
|
3020
|
+
!displayOnly && /* @__PURE__ */ jsx7(
|
|
3021
|
+
"input",
|
|
3022
|
+
{
|
|
3023
|
+
type: "text",
|
|
3024
|
+
name: "fillo_hp_field",
|
|
3025
|
+
className: "fillo-hp",
|
|
3026
|
+
tabIndex: -1,
|
|
3027
|
+
autoComplete: "off",
|
|
3028
|
+
"aria-hidden": "true",
|
|
3029
|
+
defaultValue: "",
|
|
3030
|
+
style: HP_STYLE,
|
|
3031
|
+
ref: (element) => {
|
|
3032
|
+
hpRef.current = element;
|
|
3033
|
+
assertHpHidden(element);
|
|
3034
|
+
}
|
|
3035
|
+
}
|
|
3036
|
+
),
|
|
3037
|
+
challenge && showChallenge && /* @__PURE__ */ jsxs6("div", { className: "fillo-turnstile-slot", "data-fillo": "turnstile-slot", children: [
|
|
3038
|
+
/* @__PURE__ */ jsx7(
|
|
3039
|
+
TurnstileWidget,
|
|
3040
|
+
{
|
|
3041
|
+
siteKey: challenge.siteKey,
|
|
3042
|
+
className: slotClass3("fillo-turnstile", appearance, { slot: "turnstile" }),
|
|
3043
|
+
onToken: handleChallengeToken,
|
|
3044
|
+
onError: () => setChallengeError(true),
|
|
3045
|
+
registerReset: registerChallengeReset
|
|
3046
|
+
}
|
|
3047
|
+
),
|
|
3048
|
+
challengeError && /* @__PURE__ */ jsx7("p", { className: "fillo-turnstile-error", role: "alert", children: strings.challengeUnavailable })
|
|
3049
|
+
] }),
|
|
3050
|
+
(api.status === "error" || api.submitError) && /* @__PURE__ */ jsxs6("p", { className: "fillo-submit-error", role: "alert", children: [
|
|
3051
|
+
api.submitError ?? strings.submitFailed,
|
|
3052
|
+
submitFixUrl && /* @__PURE__ */ jsxs6(Fragment3, { children: [
|
|
3053
|
+
" ",
|
|
3054
|
+
"Connect storage:",
|
|
3055
|
+
" ",
|
|
3056
|
+
/* @__PURE__ */ jsx7("a", { href: submitFixUrl, target: "_blank", rel: "noopener noreferrer", children: submitFixUrl })
|
|
3057
|
+
] })
|
|
3058
|
+
] }),
|
|
3059
|
+
showFooter && /* @__PURE__ */ jsxs6("footer", { className: slotClass3("fillo-footer", appearance, { slot: "footer" }), "data-fillo": "footer", children: [
|
|
3060
|
+
multiPage && !api.isFirstPage && /* @__PURE__ */ jsx7(
|
|
3061
|
+
"button",
|
|
3062
|
+
{
|
|
3063
|
+
type: "button",
|
|
3064
|
+
className: slotClass3("fillo-button fillo-button--ghost", appearance, { slot: "button", variant: "ghost" }),
|
|
3065
|
+
"data-fillo": "button",
|
|
3066
|
+
onClick: api.back,
|
|
3067
|
+
children: strings.back
|
|
3068
|
+
}
|
|
3069
|
+
),
|
|
3070
|
+
showPrimaryButton && /* @__PURE__ */ jsx7(
|
|
3071
|
+
"button",
|
|
3072
|
+
{
|
|
3073
|
+
type: "submit",
|
|
3074
|
+
className: slotClass3("fillo-button fillo-button--primary", appearance, { slot: "button", variant: "primary" }),
|
|
3075
|
+
"data-fillo": "button",
|
|
3076
|
+
disabled: api.status === "submitting" || api.uploading || // Wait for the human check before enabling submit (never
|
|
3077
|
+
// "Next"). The server rejects a tokenless submit regardless —
|
|
3078
|
+
// this is UX only.
|
|
3079
|
+
showChallenge && !challengeToken,
|
|
3080
|
+
children: api.uploading ? strings.uploading : api.status === "submitting" ? strings.submitting : api.isLastPage ? settings.submitLabel ?? strings.submit : strings.next
|
|
3081
|
+
}
|
|
3082
|
+
)
|
|
3083
|
+
] }),
|
|
3084
|
+
props.poweredBy && /* @__PURE__ */ jsx7(PoweredBy, { label: props.brandLabel, href: props.brandHref })
|
|
3085
|
+
] });
|
|
3086
|
+
return /* @__PURE__ */ jsx7(FilloContext.Provider, { value: api, children: /* @__PURE__ */ jsx7(FilloChromeContext.Provider, { value: chromeValue, children: /* @__PURE__ */ jsx7(FilloAnnounceContext.Provider, { value: announce, children: /* @__PURE__ */ jsx7(FilloStringsContext.Provider, { value: strings, children: /* @__PURE__ */ jsx7(FilloAppearanceContext.Provider, { value: appearance, children: /* @__PURE__ */ jsx7(FilloInstanceIdContext.Provider, { value: instanceId, children: displayOnly ? (
|
|
3087
|
+
// Display-only preview: a plain div (no form element to submit) with
|
|
3088
|
+
// every control inside a natively disabled fieldset — unfocusable
|
|
3089
|
+
// and unfillable even without the stylesheet or inert support.
|
|
3090
|
+
/* @__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 }) })
|
|
3091
|
+
) : /* @__PURE__ */ jsx7(
|
|
2438
3092
|
"form",
|
|
2439
3093
|
{
|
|
2440
|
-
className:
|
|
2441
|
-
slot: "root",
|
|
2442
|
-
status: api.status
|
|
2443
|
-
}),
|
|
3094
|
+
className: rootClass,
|
|
2444
3095
|
"data-fillo": "root",
|
|
2445
3096
|
"data-state": api.status,
|
|
2446
3097
|
"data-page": api.pageIndex + 1,
|
|
@@ -2451,159 +3102,35 @@ function ResolvedForm(props) {
|
|
|
2451
3102
|
e.preventDefault();
|
|
2452
3103
|
const formEl = e.currentTarget;
|
|
2453
3104
|
if (api.isLastPage) {
|
|
2454
|
-
void api.submit().then(() =>
|
|
3105
|
+
void api.submit().then(() => focusErrorSummary(formEl)).catch(() => {
|
|
2455
3106
|
});
|
|
2456
3107
|
} else {
|
|
2457
3108
|
api.next();
|
|
2458
|
-
|
|
3109
|
+
focusErrorSummary(formEl);
|
|
2459
3110
|
}
|
|
2460
3111
|
},
|
|
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
|
-
]
|
|
3112
|
+
children: formBody
|
|
2587
3113
|
}
|
|
2588
|
-
) }) }) }) });
|
|
3114
|
+
) }) }) }) }) }) });
|
|
2589
3115
|
}
|
|
2590
3116
|
|
|
2591
3117
|
// src/jsx.tsx
|
|
2592
|
-
import { useRef as
|
|
3118
|
+
import { useRef as useRef9 } from "react";
|
|
2593
3119
|
import {
|
|
2594
3120
|
codeFormFromJsx,
|
|
2595
3121
|
FilloJsxError,
|
|
3122
|
+
isLikelyDevEnv as isLikelyDevEnv2,
|
|
2596
3123
|
JSX_BLOCK_COMPONENTS
|
|
2597
3124
|
} from "@usefillo/core";
|
|
2598
|
-
import { jsx as
|
|
3125
|
+
import { jsx as jsx8 } from "react/jsx-runtime";
|
|
2599
3126
|
var B = JSX_BLOCK_COMPONENTS;
|
|
2600
|
-
var
|
|
3127
|
+
var isDevEnv = () => isLikelyDevEnv2();
|
|
2601
3128
|
function JsxForm(props) {
|
|
2602
3129
|
const { id, title, description, settings, theme, client, children, ...rest } = props;
|
|
2603
3130
|
const compiled = codeFormFromJsx({ id, title, description, settings, theme }, children);
|
|
2604
3131
|
const json = JSON.stringify(compiled.schema) + JSON.stringify(compiled.theme ?? null);
|
|
2605
|
-
const memo2 =
|
|
2606
|
-
if (memo2.current && memo2.current.json !== json &&
|
|
3132
|
+
const memo2 = useRef9(null);
|
|
3133
|
+
if (memo2.current && memo2.current.json !== json && isDevEnv()) {
|
|
2607
3134
|
console.warn(
|
|
2608
3135
|
`[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
3136
|
);
|
|
@@ -2611,7 +3138,7 @@ function JsxForm(props) {
|
|
|
2611
3138
|
if (!memo2.current || memo2.current.json !== json) {
|
|
2612
3139
|
memo2.current = { json, form: compiled };
|
|
2613
3140
|
}
|
|
2614
|
-
return /* @__PURE__ */
|
|
3141
|
+
return /* @__PURE__ */ jsx8(FilloForm, { form: memo2.current.form, client, ...rest });
|
|
2615
3142
|
}
|
|
2616
3143
|
function defineFormFromJsx(element) {
|
|
2617
3144
|
if (!element || element.type !== JsxForm) {
|
|
@@ -2646,6 +3173,7 @@ var Fillo = {
|
|
|
2646
3173
|
Date: B.Date,
|
|
2647
3174
|
FileUpload: B.FileUpload,
|
|
2648
3175
|
Hidden: B.Hidden,
|
|
3176
|
+
Calculated: B.Calculated,
|
|
2649
3177
|
Custom: B.Custom,
|
|
2650
3178
|
Heading: B.Heading,
|
|
2651
3179
|
Paragraph: B.Paragraph,
|
|
@@ -2655,7 +3183,7 @@ var Fillo = {
|
|
|
2655
3183
|
};
|
|
2656
3184
|
|
|
2657
3185
|
// src/provider.tsx
|
|
2658
|
-
import { useId as useId3, useMemo as useMemo6 } from "react";
|
|
3186
|
+
import { useId as useId3, useMemo as useMemo6, useState as useState7 } from "react";
|
|
2659
3187
|
import {
|
|
2660
3188
|
FilloError as FilloError3,
|
|
2661
3189
|
formSchemasEqual as formSchemasEqual2,
|
|
@@ -2664,7 +3192,7 @@ import {
|
|
|
2664
3192
|
resolveStrings as resolveStrings2,
|
|
2665
3193
|
syncCodeForm as syncCodeForm3
|
|
2666
3194
|
} from "@usefillo/core";
|
|
2667
|
-
import { Fragment as
|
|
3195
|
+
import { Fragment as Fragment4, jsx as jsx9 } from "react/jsx-runtime";
|
|
2668
3196
|
function FilloProvider({
|
|
2669
3197
|
children,
|
|
2670
3198
|
form,
|
|
@@ -2674,20 +3202,31 @@ function FilloProvider({
|
|
|
2674
3202
|
strings,
|
|
2675
3203
|
onError,
|
|
2676
3204
|
renderError,
|
|
3205
|
+
preview,
|
|
2677
3206
|
...options
|
|
2678
3207
|
}) {
|
|
2679
3208
|
const instanceId = useId3().replace(/:/g, "");
|
|
3209
|
+
const devEnv = useIsDevEnv();
|
|
3210
|
+
const devChrome = preview === true || devEnv;
|
|
3211
|
+
if (preview === true) warnPreviewInProduction();
|
|
2680
3212
|
const codeForm = isCodeForm2(form) ? form : null;
|
|
2681
3213
|
const {
|
|
2682
3214
|
formId: syncedFormId,
|
|
2683
3215
|
status: syncedStatus,
|
|
2684
3216
|
error: syncError,
|
|
2685
3217
|
syncing: codeFormSyncing,
|
|
2686
|
-
resolvedSchema
|
|
3218
|
+
resolvedSchema,
|
|
3219
|
+
accepting: syncedAccepting,
|
|
3220
|
+
acceptingReason: syncedAcceptingReason,
|
|
3221
|
+
uploadsAvailable,
|
|
3222
|
+
uploadFileSizeLimitMb,
|
|
3223
|
+
warningCode,
|
|
3224
|
+
warningUrl
|
|
2687
3225
|
} = useCodeFormSync(form, client, onError, {
|
|
2688
3226
|
allowUnsynced: Boolean(!client || formId || options.skipValidation)
|
|
2689
3227
|
});
|
|
2690
|
-
const
|
|
3228
|
+
const [resyncWarning, setResyncWarning] = useState7(null);
|
|
3229
|
+
const rawSchema = codeForm ? resolvedSchema && !devChrome ? resolvedSchema : codeForm.schema : form;
|
|
2691
3230
|
const normalized = useMemo6(() => normalizeFormSchema2(rawSchema), [rawSchema]);
|
|
2692
3231
|
if (!normalized.ok) {
|
|
2693
3232
|
throw new FilloError3(`This form could not be rendered: ${normalized.error}`, 422);
|
|
@@ -2697,6 +3236,12 @@ function FilloProvider({
|
|
|
2697
3236
|
() => codeForm && client?.key ? async () => {
|
|
2698
3237
|
try {
|
|
2699
3238
|
const result = await syncCodeForm3(client, codeForm, { bypassCache: true });
|
|
3239
|
+
setResyncWarning({
|
|
3240
|
+
code: result.warningCode,
|
|
3241
|
+
url: result.warningUrl,
|
|
3242
|
+
uploadsAvailable: result.uploadsAvailable,
|
|
3243
|
+
uploadFileSizeLimitMb: result.uploadFileSizeLimitMb
|
|
3244
|
+
});
|
|
2700
3245
|
if (result.status === "draft") {
|
|
2701
3246
|
throw new FilloError3(
|
|
2702
3247
|
"This form is no longer published. Submission was stopped before sending answers.",
|
|
@@ -2705,6 +3250,14 @@ function FilloProvider({
|
|
|
2705
3250
|
"form_not_published"
|
|
2706
3251
|
);
|
|
2707
3252
|
}
|
|
3253
|
+
if (result.accepting === false) {
|
|
3254
|
+
throw new FilloError3(
|
|
3255
|
+
"This form is no longer accepting responses. Submission was stopped before sending answers.",
|
|
3256
|
+
403,
|
|
3257
|
+
void 0,
|
|
3258
|
+
result.acceptingReason ?? "form_not_accepting"
|
|
3259
|
+
);
|
|
3260
|
+
}
|
|
2708
3261
|
const authoritative = normalizeFormSchema2(result.resolvedSchema ?? codeForm.schema);
|
|
2709
3262
|
if (!authoritative.ok) {
|
|
2710
3263
|
throw new FilloError3(
|
|
@@ -2752,25 +3305,45 @@ function FilloProvider({
|
|
|
2752
3305
|
form: schema,
|
|
2753
3306
|
formId: syncedFormId ?? formId,
|
|
2754
3307
|
resolveFormId,
|
|
3308
|
+
// Dev chrome (preview or dev env) surfaces the developer-grade resolution
|
|
3309
|
+
// failure (message + code); an explicit option wins.
|
|
3310
|
+
verboseResolutionErrors: options.verboseResolutionErrors ?? devChrome,
|
|
2755
3311
|
// Headless = your markup, no Fillo-rendered layout. Free like every embed
|
|
2756
3312
|
// method; recorded per response so usage stays measurable.
|
|
2757
3313
|
surface: "headless"
|
|
2758
3314
|
});
|
|
2759
3315
|
useDraftFlush(api);
|
|
3316
|
+
const freshWarningCode = resyncWarning ? resyncWarning.code : warningCode;
|
|
3317
|
+
const freshWarningUrl = resyncWarning ? resyncWarning.url : warningUrl;
|
|
3318
|
+
const freshUploadsAvailable = resyncWarning ? resyncWarning.uploadsAvailable : uploadsAvailable;
|
|
3319
|
+
const freshUploadFileSizeLimitMb = resyncWarning ? resyncWarning.uploadFileSizeLimitMb : uploadFileSizeLimitMb;
|
|
3320
|
+
const chromeValue = useMemo6(
|
|
3321
|
+
() => ({
|
|
3322
|
+
devChrome,
|
|
3323
|
+
uploadsAvailable: freshUploadsAvailable,
|
|
3324
|
+
uploadFileSizeLimitMb: freshUploadFileSizeLimitMb,
|
|
3325
|
+
warningCode: freshWarningCode,
|
|
3326
|
+
warningUrl: freshWarningUrl
|
|
3327
|
+
}),
|
|
3328
|
+
[devChrome, freshUploadsAvailable, freshUploadFileSizeLimitMb, freshWarningCode, freshWarningUrl]
|
|
3329
|
+
);
|
|
2760
3330
|
const resolvedStrings = resolveStrings2(strings);
|
|
2761
3331
|
const canonicalSyncError = syncError && !options.skipValidation ? syncError : null;
|
|
2762
|
-
if (canonicalSyncError && renderError) return /* @__PURE__ */
|
|
2763
|
-
if (canonicalSyncError && !
|
|
2764
|
-
if (codeFormSyncing && !options.skipValidation && !
|
|
3332
|
+
if (canonicalSyncError && renderError) return /* @__PURE__ */ jsx9(Fragment4, { children: renderError(canonicalSyncError) });
|
|
3333
|
+
if (canonicalSyncError && !devChrome) return null;
|
|
3334
|
+
if (codeFormSyncing && !options.skipValidation && !devChrome) return null;
|
|
2765
3335
|
const notPublished = Boolean(codeForm) && syncedStatus === "draft" && !options.skipValidation;
|
|
2766
|
-
|
|
2767
|
-
|
|
2768
|
-
|
|
3336
|
+
const notAccepting = Boolean(codeForm) && syncedStatus === "published" && syncedAccepting === false && !options.skipValidation;
|
|
3337
|
+
if ((notPublished || notAccepting) && !devChrome) {
|
|
3338
|
+
const error = notPublished ? new FilloError3("This form isn't published yet.", 403, void 0, "form_not_published") : new FilloError3(
|
|
3339
|
+
"This form is no longer accepting responses.",
|
|
3340
|
+
403,
|
|
3341
|
+
void 0,
|
|
3342
|
+
syncedAcceptingReason ?? "form_not_accepting"
|
|
3343
|
+
);
|
|
3344
|
+
return renderError ? /* @__PURE__ */ jsx9(Fragment4, { children: renderError(error) }) : null;
|
|
2769
3345
|
}
|
|
2770
|
-
return /* @__PURE__ */
|
|
2771
|
-
}
|
|
2772
|
-
function isDevEnv3() {
|
|
2773
|
-
return typeof process !== "undefined" && process.env?.NODE_ENV !== "production";
|
|
3346
|
+
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
3347
|
}
|
|
2775
3348
|
|
|
2776
3349
|
// src/index.ts
|