@usefillo/react 0.10.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/dist/index.d.ts +8 -1
- package/dist/index.js +413 -132
- package/dist/styles.css +214 -50
- package/dist/styles.unlayered.css +214 -50
- package/package.json +3 -2
package/dist/index.js
CHANGED
|
@@ -6,7 +6,7 @@ import {
|
|
|
6
6
|
useEffect as useEffect8,
|
|
7
7
|
useId as useId2,
|
|
8
8
|
useMemo as useMemo5,
|
|
9
|
-
useRef as
|
|
9
|
+
useRef as useRef8,
|
|
10
10
|
useState as useState6,
|
|
11
11
|
useSyncExternalStore as useSyncExternalStore2
|
|
12
12
|
} from "react";
|
|
@@ -15,15 +15,17 @@ import {
|
|
|
15
15
|
createClient,
|
|
16
16
|
formSchemasEqual,
|
|
17
17
|
isBuildTimeDevEnv as isBuildTimeDevEnv2,
|
|
18
|
+
isField as isField2,
|
|
18
19
|
isFilloError as isFilloError2,
|
|
19
20
|
isLikelyDevEnv,
|
|
20
21
|
needsExplicitSubmit,
|
|
21
22
|
normalizeFormSchema,
|
|
22
23
|
normalizeFormTheme,
|
|
23
24
|
reachableFields,
|
|
24
|
-
reachablePageSequence
|
|
25
|
+
reachablePageSequence,
|
|
26
|
+
resolveThemeAppearance
|
|
25
27
|
} from "@usefillo/core";
|
|
26
|
-
import { resolveStrings, slotClass as slotClass3 } from "@usefillo/core";
|
|
28
|
+
import { REQUIRED_FIELD_MESSAGE as REQUIRED_FIELD_MESSAGE2, resolveStrings, slotClass as slotClass3 } from "@usefillo/core";
|
|
27
29
|
|
|
28
30
|
// src/appearance.tsx
|
|
29
31
|
import { createContext, useContext } from "react";
|
|
@@ -155,6 +157,12 @@ var FilloChromeContext = createContext2(null);
|
|
|
155
157
|
function useFilloChrome() {
|
|
156
158
|
return useContext2(FilloChromeContext);
|
|
157
159
|
}
|
|
160
|
+
var noopAnnounce = () => {
|
|
161
|
+
};
|
|
162
|
+
var FilloAnnounceContext = createContext2(noopAnnounce);
|
|
163
|
+
function useFilloAnnounce() {
|
|
164
|
+
return useContext2(FilloAnnounceContext);
|
|
165
|
+
}
|
|
158
166
|
function useFilloInstanceId() {
|
|
159
167
|
const instanceId = useContext2(FilloInstanceIdContext);
|
|
160
168
|
const fallbackId = useId().replace(/:/g, "");
|
|
@@ -328,6 +336,7 @@ function useCodeFormSync(form, client, onError, options) {
|
|
|
328
336
|
accepting: void 0,
|
|
329
337
|
acceptingReason: void 0,
|
|
330
338
|
uploadsAvailable: void 0,
|
|
339
|
+
uploadFileSizeLimitMb: void 0,
|
|
331
340
|
resolvedSchema: void 0,
|
|
332
341
|
resolvedTheme: void 0,
|
|
333
342
|
warning: void 0,
|
|
@@ -390,6 +399,7 @@ function useCodeFormSync(form, client, onError, options) {
|
|
|
390
399
|
accepting: r.accepting,
|
|
391
400
|
acceptingReason: r.acceptingReason,
|
|
392
401
|
uploadsAvailable: r.uploadsAvailable,
|
|
402
|
+
uploadFileSizeLimitMb: r.uploadFileSizeLimitMb,
|
|
393
403
|
resolvedSchema: r.resolvedSchema,
|
|
394
404
|
resolvedTheme: r.resolvedTheme,
|
|
395
405
|
warning: r.warning,
|
|
@@ -451,6 +461,7 @@ function useCodeFormSync(form, client, onError, options) {
|
|
|
451
461
|
accepting: synced.accepting,
|
|
452
462
|
acceptingReason: synced.acceptingReason,
|
|
453
463
|
uploadsAvailable: synced.uploadsAvailable,
|
|
464
|
+
uploadFileSizeLimitMb: synced.uploadFileSizeLimitMb,
|
|
454
465
|
resolvedSchema: synced.resolvedSchema,
|
|
455
466
|
resolvedTheme: synced.resolvedTheme,
|
|
456
467
|
warning: synced.warning,
|
|
@@ -467,12 +478,25 @@ function useCodeFormSync(form, client, onError, options) {
|
|
|
467
478
|
}
|
|
468
479
|
|
|
469
480
|
// src/fields.tsx
|
|
470
|
-
import {
|
|
471
|
-
|
|
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";
|
|
472
495
|
|
|
473
496
|
// src/phone.tsx
|
|
474
497
|
import {
|
|
475
498
|
PHONE_COUNTRIES,
|
|
499
|
+
PHONE_PICKER_COUNTRIES,
|
|
476
500
|
countryByIso,
|
|
477
501
|
countryByTimeZone,
|
|
478
502
|
digitsOnly,
|
|
@@ -524,8 +548,9 @@ function PhoneField({ field, value, error, setValue, ids: providedIds }) {
|
|
|
524
548
|
const guess = countryByTimeZone(browserTimeZone()) ?? countryByIso(localeCountry());
|
|
525
549
|
if (guess) setCountry(guess);
|
|
526
550
|
}, []);
|
|
551
|
+
const [pendingRaw, setPendingRaw] = useState2(null);
|
|
527
552
|
const national = nationalFor(stored, country);
|
|
528
|
-
const display = formatNational(country, national);
|
|
553
|
+
const display = pendingRaw ?? formatNational(country, national);
|
|
529
554
|
const inputRef = useRef3(null);
|
|
530
555
|
const caretDigits = useRef3(null);
|
|
531
556
|
useLayoutEffect(() => {
|
|
@@ -552,15 +577,23 @@ function PhoneField({ field, value, error, setValue, ids: providedIds }) {
|
|
|
552
577
|
const caret = el.selectionStart ?? raw.length;
|
|
553
578
|
caretDigits.current = digitsOnly(raw.slice(0, caret)).length;
|
|
554
579
|
if (raw.trimStart().startsWith("+")) {
|
|
555
|
-
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);
|
|
556
587
|
if (p.country) setCountry(p.country);
|
|
557
588
|
commit(p.country ?? country, p.country ? p.national : digitsOnly(raw));
|
|
558
589
|
return;
|
|
559
590
|
}
|
|
591
|
+
setPendingRaw(null);
|
|
560
592
|
commit(country, digitsOnly(raw));
|
|
561
593
|
}
|
|
562
594
|
function pickCountry(c) {
|
|
563
595
|
touched.current = true;
|
|
596
|
+
setPendingRaw(null);
|
|
564
597
|
setCountry(c);
|
|
565
598
|
commit(c, national);
|
|
566
599
|
inputRef.current?.focus();
|
|
@@ -593,7 +626,7 @@ function PhoneField({ field, value, error, setValue, ids: providedIds }) {
|
|
|
593
626
|
}
|
|
594
627
|
)
|
|
595
628
|
] }),
|
|
596
|
-
error && /* @__PURE__ */ jsx2("p", { className: slots.error, "data-fillo": "error", id: ids.errorId,
|
|
629
|
+
error && /* @__PURE__ */ jsx2("p", { className: slots.error, "data-fillo": "error", id: ids.errorId, children: error })
|
|
597
630
|
] });
|
|
598
631
|
}
|
|
599
632
|
function CountrySelect({
|
|
@@ -601,6 +634,8 @@ function CountrySelect({
|
|
|
601
634
|
onPick,
|
|
602
635
|
labelledBy
|
|
603
636
|
}) {
|
|
637
|
+
const strings = useStrings();
|
|
638
|
+
const announce = useFilloAnnounce();
|
|
604
639
|
const [open, setOpen] = useState2(false);
|
|
605
640
|
const [query, setQuery] = useState2("");
|
|
606
641
|
const [active, setActive] = useState2(0);
|
|
@@ -609,19 +644,22 @@ function CountrySelect({
|
|
|
609
644
|
const triggerRef = useRef3(null);
|
|
610
645
|
const popoverRef = useRef3(null);
|
|
611
646
|
const searchRef = useRef3(null);
|
|
647
|
+
const seedRef = useRef3(null);
|
|
612
648
|
const listId = `${labelledBy}-countries`;
|
|
613
649
|
const matches = useMemo3(() => {
|
|
614
650
|
const q = query.trim().toLowerCase();
|
|
615
|
-
if (!q) return
|
|
651
|
+
if (!q) return PHONE_PICKER_COUNTRIES;
|
|
616
652
|
const qDigits = q.replace(/[^\d]/g, "");
|
|
617
|
-
return
|
|
653
|
+
return PHONE_PICKER_COUNTRIES.filter(
|
|
618
654
|
(c) => c.name.toLowerCase().includes(q) || c.iso2.toLowerCase() === q || qDigits.length > 0 && c.dialCode.startsWith(qDigits)
|
|
619
655
|
);
|
|
620
656
|
}, [query]);
|
|
621
657
|
useEffect3(() => {
|
|
622
658
|
if (!open) return;
|
|
623
|
-
|
|
624
|
-
|
|
659
|
+
const seed = seedRef.current ?? "";
|
|
660
|
+
seedRef.current = null;
|
|
661
|
+
setQuery(seed);
|
|
662
|
+
setActive(seed ? 0 : Math.max(0, PHONE_PICKER_COUNTRIES.indexOf(country)));
|
|
625
663
|
const id = requestAnimationFrame(() => searchRef.current?.focus());
|
|
626
664
|
return () => cancelAnimationFrame(id);
|
|
627
665
|
}, [open, country]);
|
|
@@ -648,8 +686,15 @@ function CountrySelect({
|
|
|
648
686
|
if (!open) return;
|
|
649
687
|
document.getElementById(`${listId}-opt-${active}`)?.scrollIntoView({ block: "nearest" });
|
|
650
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]);
|
|
651
695
|
function choose(c) {
|
|
652
696
|
if (!c) return;
|
|
697
|
+
announce(strings.phoneCountrySelected(c.name));
|
|
653
698
|
onPick(c);
|
|
654
699
|
setOpen(false);
|
|
655
700
|
}
|
|
@@ -669,7 +714,30 @@ function CountrySelect({
|
|
|
669
714
|
triggerRef.current?.focus();
|
|
670
715
|
}
|
|
671
716
|
}
|
|
672
|
-
|
|
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: [
|
|
673
741
|
/* @__PURE__ */ jsxs2(
|
|
674
742
|
"button",
|
|
675
743
|
{
|
|
@@ -681,6 +749,7 @@ function CountrySelect({
|
|
|
681
749
|
"aria-controls": open ? listId : void 0,
|
|
682
750
|
"aria-label": `Country: ${country.name} (+${country.dialCode})`,
|
|
683
751
|
onClick: () => setOpen((o) => !o),
|
|
752
|
+
onKeyDown: onTriggerKeyDown,
|
|
684
753
|
children: [
|
|
685
754
|
/* @__PURE__ */ jsx2("span", { className: "fillo-phone-flag-emoji", "aria-hidden": "true", children: flagEmoji(country.iso2) }),
|
|
686
755
|
/* @__PURE__ */ jsxs2("span", { className: "fillo-phone-dial", children: [
|
|
@@ -737,7 +806,7 @@ function CountrySelect({
|
|
|
737
806
|
},
|
|
738
807
|
c.iso2
|
|
739
808
|
)),
|
|
740
|
-
matches.length === 0 && /* @__PURE__ */ jsx2("li", { className: "fillo-phone-empty", children: "No matches" })
|
|
809
|
+
matches.length === 0 && /* @__PURE__ */ jsx2("li", { className: "fillo-phone-empty", role: "option", "aria-disabled": "true", children: "No matches" })
|
|
741
810
|
] })
|
|
742
811
|
] })
|
|
743
812
|
] });
|
|
@@ -752,6 +821,7 @@ function SignatureField({ field, value, error, setValue, ids: providedIds }) {
|
|
|
752
821
|
const strings = useStrings();
|
|
753
822
|
const typedInputId = `${ids.inputId}-typed`;
|
|
754
823
|
const canvasRef = useRef4(null);
|
|
824
|
+
const typedInputRef = useRef4(null);
|
|
755
825
|
const drawing = useRef4(false);
|
|
756
826
|
const [hasInk, setHasInk] = useState3(Boolean(value));
|
|
757
827
|
const [typedName, setTypedName] = useState3("");
|
|
@@ -848,6 +918,7 @@ function SignatureField({ field, value, error, setValue, ids: providedIds }) {
|
|
|
848
918
|
setHasInk(false);
|
|
849
919
|
setTypedName("");
|
|
850
920
|
setValue(null);
|
|
921
|
+
typedInputRef.current?.focus();
|
|
851
922
|
}
|
|
852
923
|
const describedBy = [field.description ? ids.descriptionId : null, error ? ids.errorId : null].filter(Boolean).join(" ") || void 0;
|
|
853
924
|
return /* @__PURE__ */ jsxs3("div", { ...slots.wrapperProps(`fillo-field fillo-field--signature${error ? " fillo-field--error" : ""}`), children: [
|
|
@@ -869,7 +940,8 @@ function SignatureField({ field, value, error, setValue, ids: providedIds }) {
|
|
|
869
940
|
{
|
|
870
941
|
ref: canvasRef,
|
|
871
942
|
className: "fillo-signature-canvas",
|
|
872
|
-
|
|
943
|
+
role: "img",
|
|
944
|
+
"aria-label": hasInk ? strings.signatureSigned : strings.signatureEmpty,
|
|
873
945
|
onPointerDown: start,
|
|
874
946
|
onPointerMove: move,
|
|
875
947
|
onPointerUp: end,
|
|
@@ -886,6 +958,7 @@ function SignatureField({ field, value, error, setValue, ids: providedIds }) {
|
|
|
886
958
|
"input",
|
|
887
959
|
{
|
|
888
960
|
id: typedInputId,
|
|
961
|
+
ref: typedInputRef,
|
|
889
962
|
type: "text",
|
|
890
963
|
className: "fillo-input fillo-signature-type-input",
|
|
891
964
|
autoComplete: "name",
|
|
@@ -904,7 +977,7 @@ function SignatureField({ field, value, error, setValue, ids: providedIds }) {
|
|
|
904
977
|
]
|
|
905
978
|
}
|
|
906
979
|
),
|
|
907
|
-
error && /* @__PURE__ */ jsx3("p", { className: slots.error, "data-fillo": "error", id: ids.errorId,
|
|
980
|
+
error && /* @__PURE__ */ jsx3("p", { className: slots.error, "data-fillo": "error", id: ids.errorId, children: error })
|
|
908
981
|
] });
|
|
909
982
|
}
|
|
910
983
|
|
|
@@ -926,6 +999,14 @@ function formatBytes(bytes) {
|
|
|
926
999
|
if (bytes < 1024 ** 3) return `${(bytes / 1024 ** 2).toFixed(1)} MB`;
|
|
927
1000
|
return `${(bytes / 1024 ** 3).toFixed(2)} GB`;
|
|
928
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
|
+
}
|
|
929
1010
|
function FileUploadField({ field, value, error, setValue, api, ids: providedIds }) {
|
|
930
1011
|
const ids = useFilloFieldIds(field.id, providedIds);
|
|
931
1012
|
const slots = useFieldSlots(field, error);
|
|
@@ -952,7 +1033,8 @@ function FileUploadField({ field, value, error, setValue, api, ids: providedIds
|
|
|
952
1033
|
const filesRef = useRef5(files);
|
|
953
1034
|
filesRef.current = files;
|
|
954
1035
|
const maxFiles = schema.maxFiles ?? 1;
|
|
955
|
-
const
|
|
1036
|
+
const configuredMaxMb = schema.maxFileSizeMb ?? 500;
|
|
1037
|
+
const maxMb = chrome?.uploadFileSizeLimitMb ? Math.min(configuredMaxMb, chrome.uploadFileSizeLimitMb) : configuredMaxMb;
|
|
956
1038
|
const maxBytes = maxMb * 1024 * 1024;
|
|
957
1039
|
const storageBlocked = chrome?.uploadsAvailable === false;
|
|
958
1040
|
const storageUrl = storageBlocked && chrome?.devChrome ? safeHttpUrl(chrome.warningUrl) : null;
|
|
@@ -991,7 +1073,7 @@ function FileUploadField({ field, value, error, setValue, api, ids: providedIds
|
|
|
991
1073
|
setValue(next);
|
|
992
1074
|
} catch (err) {
|
|
993
1075
|
if (mountedRef.current) {
|
|
994
|
-
const message = err
|
|
1076
|
+
const message = uploadFailureMessage(err, strings);
|
|
995
1077
|
setInFlight(
|
|
996
1078
|
(prev) => prev.map((f) => f.key === key ? { ...f, error: message } : f)
|
|
997
1079
|
);
|
|
@@ -1095,6 +1177,7 @@ function FileUploadField({ field, value, error, setValue, api, ids: providedIds
|
|
|
1095
1177
|
files.map((f) => /* @__PURE__ */ jsxs4("li", { className: "fillo-file fillo-file--done", children: [
|
|
1096
1178
|
/* @__PURE__ */ jsx4("span", { className: "fillo-file-name", children: f.name }),
|
|
1097
1179
|
/* @__PURE__ */ jsx4("span", { className: "fillo-file-meta", children: formatBytes(f.size) }),
|
|
1180
|
+
/* @__PURE__ */ jsx4("span", { className: "fillo-sr-only", children: "Uploaded" }),
|
|
1098
1181
|
/* @__PURE__ */ jsx4(
|
|
1099
1182
|
"button",
|
|
1100
1183
|
{
|
|
@@ -1120,32 +1203,34 @@ function FileUploadField({ field, value, error, setValue, api, ids: providedIds
|
|
|
1120
1203
|
children: [
|
|
1121
1204
|
/* @__PURE__ */ jsx4("span", { className: "fillo-file-name", children: f.name }),
|
|
1122
1205
|
f.error ? /* @__PURE__ */ jsxs4(Fragment2, { children: [
|
|
1123
|
-
/* @__PURE__ */
|
|
1124
|
-
|
|
1125
|
-
|
|
1126
|
-
|
|
1127
|
-
|
|
1128
|
-
|
|
1129
|
-
|
|
1130
|
-
|
|
1131
|
-
|
|
1132
|
-
|
|
1133
|
-
|
|
1134
|
-
|
|
1135
|
-
|
|
1136
|
-
|
|
1137
|
-
|
|
1138
|
-
|
|
1139
|
-
|
|
1140
|
-
|
|
1141
|
-
|
|
1142
|
-
|
|
1143
|
-
|
|
1144
|
-
|
|
1145
|
-
|
|
1146
|
-
|
|
1147
|
-
|
|
1148
|
-
|
|
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 })
|
|
1149
1234
|
] }) : /* @__PURE__ */ jsxs4(Fragment2, { children: [
|
|
1150
1235
|
/* @__PURE__ */ jsxs4("span", { className: "fillo-file-meta", children: [
|
|
1151
1236
|
Math.round(f.fraction * 100),
|
|
@@ -1169,7 +1254,7 @@ function FileUploadField({ field, value, error, setValue, api, ids: providedIds
|
|
|
1169
1254
|
f.key
|
|
1170
1255
|
))
|
|
1171
1256
|
] }),
|
|
1172
|
-
error && /* @__PURE__ */ jsx4("p", { className: slots.error, "data-fillo": "error", id: ids.errorId,
|
|
1257
|
+
error && /* @__PURE__ */ jsx4("p", { className: slots.error, "data-fillo": "error", id: ids.errorId, children: error })
|
|
1173
1258
|
] });
|
|
1174
1259
|
}
|
|
1175
1260
|
|
|
@@ -1198,9 +1283,6 @@ function useControl(base, field, error) {
|
|
|
1198
1283
|
"data-fillo": "control"
|
|
1199
1284
|
};
|
|
1200
1285
|
}
|
|
1201
|
-
function isNativeControlTarget(target) {
|
|
1202
|
-
return target instanceof HTMLElement && Boolean(target.closest("input, textarea, select, button"));
|
|
1203
|
-
}
|
|
1204
1286
|
function FieldShell({
|
|
1205
1287
|
field,
|
|
1206
1288
|
error,
|
|
@@ -1237,7 +1319,7 @@ function FieldShell({
|
|
|
1237
1319
|
] }),
|
|
1238
1320
|
field.description && /* @__PURE__ */ jsx5("p", { className: descriptionCls, "data-fillo": "fieldDescription", id: ids.descriptionId, children: field.description }),
|
|
1239
1321
|
children,
|
|
1240
|
-
error && /* @__PURE__ */ jsx5("p", { className: errorCls, "data-fillo": "error", id: ids.errorId,
|
|
1322
|
+
error && /* @__PURE__ */ jsx5("p", { className: errorCls, "data-fillo": "error", id: ids.errorId, children: error })
|
|
1241
1323
|
]
|
|
1242
1324
|
}
|
|
1243
1325
|
);
|
|
@@ -1245,6 +1327,7 @@ function FieldShell({
|
|
|
1245
1327
|
function TextInput({ field, value, error, setValue, ids: providedIds }) {
|
|
1246
1328
|
const ids = useFilloFieldIds(field.id, providedIds);
|
|
1247
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;
|
|
1248
1331
|
const control = useControl("fillo-input", field, error);
|
|
1249
1332
|
return /* @__PURE__ */ jsx5(FieldShell, { field, error, ids, children: /* @__PURE__ */ jsx5(
|
|
1250
1333
|
"input",
|
|
@@ -1252,6 +1335,7 @@ function TextInput({ field, value, error, setValue, ids: providedIds }) {
|
|
|
1252
1335
|
id: ids.inputId,
|
|
1253
1336
|
...control,
|
|
1254
1337
|
type,
|
|
1338
|
+
autoComplete,
|
|
1255
1339
|
value: typeof value === "string" || typeof value === "number" ? String(value) : "",
|
|
1256
1340
|
placeholder: field.placeholder,
|
|
1257
1341
|
maxLength: "maxLength" in field ? field.maxLength : void 0,
|
|
@@ -1262,6 +1346,71 @@ function TextInput({ field, value, error, setValue, ids: providedIds }) {
|
|
|
1262
1346
|
}
|
|
1263
1347
|
) });
|
|
1264
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
|
+
}
|
|
1265
1414
|
function LongText({ field, value, error, setValue, ids: providedIds }) {
|
|
1266
1415
|
const ids = useFilloFieldIds(field.id, providedIds);
|
|
1267
1416
|
const control = useControl("fillo-input fillo-textarea", field, error);
|
|
@@ -1347,12 +1496,6 @@ function SingleChoice({ field, value, error, setValue, ids: providedIds }) {
|
|
|
1347
1496
|
opt.id,
|
|
1348
1497
|
value === opt.id
|
|
1349
1498
|
),
|
|
1350
|
-
onClick: (event) => {
|
|
1351
|
-
if (isNativeControlTarget(event.target)) return;
|
|
1352
|
-
event.preventDefault();
|
|
1353
|
-
setOtherOn(false);
|
|
1354
|
-
setValue(opt.id);
|
|
1355
|
-
},
|
|
1356
1499
|
children: [
|
|
1357
1500
|
/* @__PURE__ */ jsx5(
|
|
1358
1501
|
"input",
|
|
@@ -1368,15 +1511,21 @@ function SingleChoice({ field, value, error, setValue, ids: providedIds }) {
|
|
|
1368
1511
|
}
|
|
1369
1512
|
),
|
|
1370
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" }),
|
|
1371
1517
|
/* @__PURE__ */ jsx5("span", { className: slotClass2("fillo-option-label", appearance, { slot: "optionLabel", kind: field.kind, fieldId: field.id }), "data-fillo": "optionLabel", children: opt.label })
|
|
1372
1518
|
]
|
|
1373
1519
|
},
|
|
1374
1520
|
opt.id
|
|
1375
1521
|
)),
|
|
1376
|
-
choice.allowOther && //
|
|
1377
|
-
//
|
|
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).
|
|
1378
1527
|
/* @__PURE__ */ jsxs5(
|
|
1379
|
-
"
|
|
1528
|
+
"label",
|
|
1380
1529
|
{
|
|
1381
1530
|
"data-fillo": "option",
|
|
1382
1531
|
"data-selected": otherActive ? "" : void 0,
|
|
@@ -1386,7 +1535,7 @@ function SingleChoice({ field, value, error, setValue, ids: providedIds }) {
|
|
|
1386
1535
|
otherActive
|
|
1387
1536
|
),
|
|
1388
1537
|
children: [
|
|
1389
|
-
/* @__PURE__ */ jsxs5("
|
|
1538
|
+
/* @__PURE__ */ jsxs5("span", { className: "fillo-option-main", children: [
|
|
1390
1539
|
/* @__PURE__ */ jsx5(
|
|
1391
1540
|
"input",
|
|
1392
1541
|
{
|
|
@@ -1454,11 +1603,6 @@ function MultiChoice({ field, value, error, setValue, ids: providedIds }) {
|
|
|
1454
1603
|
opt.id,
|
|
1455
1604
|
selected.includes(opt.id)
|
|
1456
1605
|
),
|
|
1457
|
-
onClick: (event) => {
|
|
1458
|
-
if (isNativeControlTarget(event.target)) return;
|
|
1459
|
-
event.preventDefault();
|
|
1460
|
-
toggle(opt.id);
|
|
1461
|
-
},
|
|
1462
1606
|
children: [
|
|
1463
1607
|
/* @__PURE__ */ jsx5(
|
|
1464
1608
|
"input",
|
|
@@ -1470,15 +1614,21 @@ function MultiChoice({ field, value, error, setValue, ids: providedIds }) {
|
|
|
1470
1614
|
}
|
|
1471
1615
|
),
|
|
1472
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" }),
|
|
1473
1620
|
/* @__PURE__ */ jsx5("span", { className: slotClass2("fillo-option-label", appearance, { slot: "optionLabel", kind: field.kind, fieldId: field.id }), "data-fillo": "optionLabel", children: opt.label })
|
|
1474
1621
|
]
|
|
1475
1622
|
},
|
|
1476
1623
|
opt.id
|
|
1477
1624
|
)),
|
|
1478
|
-
choice.allowOther && //
|
|
1479
|
-
//
|
|
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).
|
|
1480
1630
|
/* @__PURE__ */ jsxs5(
|
|
1481
|
-
"
|
|
1631
|
+
"label",
|
|
1482
1632
|
{
|
|
1483
1633
|
"data-fillo": "option",
|
|
1484
1634
|
"data-selected": otherActive ? "" : void 0,
|
|
@@ -1488,7 +1638,7 @@ function MultiChoice({ field, value, error, setValue, ids: providedIds }) {
|
|
|
1488
1638
|
otherActive
|
|
1489
1639
|
),
|
|
1490
1640
|
children: [
|
|
1491
|
-
/* @__PURE__ */ jsxs5("
|
|
1641
|
+
/* @__PURE__ */ jsxs5("span", { className: "fillo-option-main", children: [
|
|
1492
1642
|
/* @__PURE__ */ jsx5(
|
|
1493
1643
|
"input",
|
|
1494
1644
|
{
|
|
@@ -1610,18 +1760,16 @@ function Checkbox({ field, value, error, setValue, ids: providedIds }) {
|
|
|
1610
1760
|
{
|
|
1611
1761
|
id: ids.inputId,
|
|
1612
1762
|
type: "checkbox",
|
|
1613
|
-
role: "switch",
|
|
1614
1763
|
className: "fillo-toggle-input",
|
|
1615
1764
|
checked: value === true,
|
|
1616
1765
|
onChange: (e) => setValue(e.target.checked),
|
|
1617
|
-
"aria-checked": value === true,
|
|
1618
1766
|
...fieldAria(field, error, ids)
|
|
1619
1767
|
}
|
|
1620
1768
|
),
|
|
1621
1769
|
/* @__PURE__ */ jsx5("span", { className: "fillo-toggle-track", "aria-hidden": "true", children: /* @__PURE__ */ jsx5("span", { className: "fillo-toggle-thumb" }) })
|
|
1622
1770
|
] }),
|
|
1623
1771
|
field.description && /* @__PURE__ */ jsx5("p", { className: slots.description, "data-fillo": "fieldDescription", id: ids.descriptionId, children: field.description }),
|
|
1624
|
-
error && /* @__PURE__ */ jsx5("p", { className: slots.error, "data-fillo": "error", id: ids.errorId,
|
|
1772
|
+
error && /* @__PURE__ */ jsx5("p", { className: slots.error, "data-fillo": "error", id: ids.errorId, children: error })
|
|
1625
1773
|
]
|
|
1626
1774
|
}
|
|
1627
1775
|
);
|
|
@@ -1651,28 +1799,21 @@ function Checkbox({ field, value, error, setValue, ids: providedIds }) {
|
|
|
1651
1799
|
] })
|
|
1652
1800
|
] }),
|
|
1653
1801
|
field.description && /* @__PURE__ */ jsx5("p", { className: slots.description, "data-fillo": "fieldDescription", id: ids.descriptionId, children: field.description }),
|
|
1654
|
-
error && /* @__PURE__ */ jsx5("p", { className: slots.error, "data-fillo": "error", id: ids.errorId,
|
|
1802
|
+
error && /* @__PURE__ */ jsx5("p", { className: slots.error, "data-fillo": "error", id: ids.errorId, children: error })
|
|
1655
1803
|
]
|
|
1656
1804
|
}
|
|
1657
1805
|
);
|
|
1658
1806
|
}
|
|
1659
1807
|
function radiogroupKeyDown(e, steps, current, setValue) {
|
|
1660
|
-
const
|
|
1661
|
-
const
|
|
1662
|
-
|
|
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;
|
|
1663
1813
|
e.preventDefault();
|
|
1664
|
-
const index = current == null ? -1 : steps.indexOf(current);
|
|
1665
|
-
let next;
|
|
1666
|
-
if (index === -1) {
|
|
1667
|
-
next = forward ? 0 : steps.length - 1;
|
|
1668
|
-
} else {
|
|
1669
|
-
next = forward ? index + 1 : index - 1;
|
|
1670
|
-
next = (next + steps.length) % steps.length;
|
|
1671
|
-
}
|
|
1672
1814
|
const value = steps[next];
|
|
1673
1815
|
if (value === void 0) return;
|
|
1674
1816
|
setValue(value);
|
|
1675
|
-
const group = e.currentTarget;
|
|
1676
1817
|
const buttons = group.querySelectorAll('[role="radio"]');
|
|
1677
1818
|
buttons[next]?.focus();
|
|
1678
1819
|
}
|
|
@@ -1693,6 +1834,7 @@ function Rating({ field, value, error, setValue, ids: providedIds }) {
|
|
|
1693
1834
|
return /* @__PURE__ */ jsx5(FieldShell, { field, error, ids, children: /* @__PURE__ */ jsx5(
|
|
1694
1835
|
"div",
|
|
1695
1836
|
{
|
|
1837
|
+
id: ids.inputId,
|
|
1696
1838
|
...control,
|
|
1697
1839
|
role: "radiogroup",
|
|
1698
1840
|
"aria-labelledby": ids.labelId,
|
|
@@ -1714,8 +1856,11 @@ function Rating({ field, value, error, setValue, ids: providedIds }) {
|
|
|
1714
1856
|
"aria-label": `${n} of ${max}`,
|
|
1715
1857
|
"aria-checked": n === current,
|
|
1716
1858
|
tabIndex: i === tabbable ? 0 : -1,
|
|
1717
|
-
onClick: () =>
|
|
1718
|
-
|
|
1859
|
+
onClick: (event) => {
|
|
1860
|
+
if (event.detail === 0 && n === current) return;
|
|
1861
|
+
setValue(n === current ? null : n);
|
|
1862
|
+
},
|
|
1863
|
+
children: n <= current ? "\u2605" : "\u2606"
|
|
1719
1864
|
},
|
|
1720
1865
|
n
|
|
1721
1866
|
))
|
|
@@ -1745,6 +1890,7 @@ function LinearScale({ field, value, error, setValue, ids: providedIds }) {
|
|
|
1745
1890
|
/* @__PURE__ */ jsx5(
|
|
1746
1891
|
"div",
|
|
1747
1892
|
{
|
|
1893
|
+
id: ids.inputId,
|
|
1748
1894
|
...control,
|
|
1749
1895
|
role: "radiogroup",
|
|
1750
1896
|
"aria-labelledby": ids.labelId,
|
|
@@ -1768,7 +1914,10 @@ function LinearScale({ field, value, error, setValue, ids: providedIds }) {
|
|
|
1768
1914
|
"aria-label": String(n),
|
|
1769
1915
|
"aria-checked": value === n,
|
|
1770
1916
|
tabIndex: i === tabbable ? 0 : -1,
|
|
1771
|
-
onClick: () =>
|
|
1917
|
+
onClick: (event) => {
|
|
1918
|
+
if (event.detail === 0 && n === current) return;
|
|
1919
|
+
setValue(value === n ? null : n);
|
|
1920
|
+
},
|
|
1772
1921
|
children: n
|
|
1773
1922
|
},
|
|
1774
1923
|
n
|
|
@@ -1783,19 +1932,34 @@ function LinearScale({ field, value, error, setValue, ids: providedIds }) {
|
|
|
1783
1932
|
}
|
|
1784
1933
|
function Ranking({ field, value, error, setValue, ids: providedIds }) {
|
|
1785
1934
|
const ids = useFilloFieldIds(field.id, providedIds);
|
|
1935
|
+
const strings = useStrings();
|
|
1936
|
+
const announce = useFilloAnnounce();
|
|
1786
1937
|
const ranking = field;
|
|
1787
1938
|
const answered = Array.isArray(value) ? value : [];
|
|
1788
1939
|
const order = [
|
|
1789
1940
|
...answered.filter((id) => ranking.options.some((o) => o.id === id)),
|
|
1790
1941
|
...ranking.options.map((o) => o.id).filter((id) => !answered.includes(id))
|
|
1791
1942
|
];
|
|
1792
|
-
|
|
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) {
|
|
1793
1954
|
const index = order.indexOf(id);
|
|
1794
1955
|
const target = index + delta;
|
|
1795
1956
|
if (target < 0 || target >= order.length) return;
|
|
1957
|
+
pressedRef.current = button;
|
|
1796
1958
|
const next = [...order];
|
|
1797
1959
|
[next[index], next[target]] = [next[target], next[index]];
|
|
1798
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));
|
|
1799
1963
|
}
|
|
1800
1964
|
return /* @__PURE__ */ jsx5(FieldShell, { field, error, ids, children: /* @__PURE__ */ jsx5(
|
|
1801
1965
|
"div",
|
|
@@ -1818,7 +1982,7 @@ function Ranking({ field, value, error, setValue, ids: providedIds }) {
|
|
|
1818
1982
|
className: "fillo-ranking-move",
|
|
1819
1983
|
"aria-label": `Move ${option.label} up`,
|
|
1820
1984
|
disabled: index === 0,
|
|
1821
|
-
onClick: () => move(id, -1),
|
|
1985
|
+
onClick: (e) => move(id, -1, e.currentTarget),
|
|
1822
1986
|
children: "\u2191"
|
|
1823
1987
|
}
|
|
1824
1988
|
),
|
|
@@ -1829,7 +1993,7 @@ function Ranking({ field, value, error, setValue, ids: providedIds }) {
|
|
|
1829
1993
|
className: "fillo-ranking-move",
|
|
1830
1994
|
"aria-label": `Move ${option.label} down`,
|
|
1831
1995
|
disabled: index === order.length - 1,
|
|
1832
|
-
onClick: () => move(id, 1),
|
|
1996
|
+
onClick: (e) => move(id, 1, e.currentTarget),
|
|
1833
1997
|
children: "\u2193"
|
|
1834
1998
|
}
|
|
1835
1999
|
)
|
|
@@ -1872,12 +2036,12 @@ function Matrix({ field, value, error, setValue, ids: providedIds }) {
|
|
|
1872
2036
|
"div",
|
|
1873
2037
|
{
|
|
1874
2038
|
role: "radiogroup",
|
|
1875
|
-
"aria-labelledby": rowLabelId
|
|
2039
|
+
"aria-labelledby": `${ids.labelId} ${rowLabelId}`,
|
|
1876
2040
|
"aria-owns": matrix.columns.map(radioId).join(" "),
|
|
1877
2041
|
children: /* @__PURE__ */ jsx5("span", { id: rowLabelId, children: row.label })
|
|
1878
2042
|
}
|
|
1879
2043
|
) }),
|
|
1880
|
-
matrix.columns.map((col) => /* @__PURE__ */ jsx5("td", { children: /* @__PURE__ */ jsx5(
|
|
2044
|
+
matrix.columns.map((col) => /* @__PURE__ */ jsx5("td", { "data-label": col.label, children: /* @__PURE__ */ jsx5("label", { className: "fillo-matrix-cell", children: /* @__PURE__ */ jsx5(
|
|
1881
2045
|
"input",
|
|
1882
2046
|
{
|
|
1883
2047
|
id: radioId(col),
|
|
@@ -1890,7 +2054,7 @@ function Matrix({ field, value, error, setValue, ids: providedIds }) {
|
|
|
1890
2054
|
checked: answers[row.id] === col.id,
|
|
1891
2055
|
onChange: () => setValue({ ...answers, [row.id]: col.id })
|
|
1892
2056
|
}
|
|
1893
|
-
) }, col.id))
|
|
2057
|
+
) }) }, col.id))
|
|
1894
2058
|
] }, row.id)
|
|
1895
2059
|
);
|
|
1896
2060
|
}) })
|
|
@@ -1901,12 +2065,45 @@ function Matrix({ field, value, error, setValue, ids: providedIds }) {
|
|
|
1901
2065
|
function Hidden() {
|
|
1902
2066
|
return null;
|
|
1903
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
|
+
}
|
|
1904
2101
|
var DEFAULT_COMPONENTS = {
|
|
1905
2102
|
short_text: TextInput,
|
|
1906
2103
|
email: TextInput,
|
|
1907
2104
|
url: TextInput,
|
|
1908
2105
|
phone: PhoneField,
|
|
1909
|
-
number:
|
|
2106
|
+
number: NumberInput,
|
|
1910
2107
|
date: TextInput,
|
|
1911
2108
|
long_text: LongText,
|
|
1912
2109
|
select: SingleChoice,
|
|
@@ -1919,7 +2116,8 @@ var DEFAULT_COMPONENTS = {
|
|
|
1919
2116
|
matrix: Matrix,
|
|
1920
2117
|
signature: SignatureField,
|
|
1921
2118
|
file_upload: FileUploadField,
|
|
1922
|
-
hidden: Hidden
|
|
2119
|
+
hidden: Hidden,
|
|
2120
|
+
calculated: Calculated
|
|
1923
2121
|
};
|
|
1924
2122
|
function ContentRenderer({ block }) {
|
|
1925
2123
|
switch (block.kind) {
|
|
@@ -2034,7 +2232,7 @@ function FormField({
|
|
|
2034
2232
|
}
|
|
2035
2233
|
|
|
2036
2234
|
// src/turnstile.tsx
|
|
2037
|
-
import { useEffect as useEffect7, useRef as
|
|
2235
|
+
import { useEffect as useEffect7, useRef as useRef7 } from "react";
|
|
2038
2236
|
import { jsx as jsx6 } from "react/jsx-runtime";
|
|
2039
2237
|
var SCRIPT_SRC = "https://challenges.cloudflare.com/turnstile/v0/api.js?render=explicit";
|
|
2040
2238
|
var SCRIPT_ID = "fillo-turnstile-script";
|
|
@@ -2093,13 +2291,13 @@ function TurnstileWidget({
|
|
|
2093
2291
|
registerReset,
|
|
2094
2292
|
className
|
|
2095
2293
|
}) {
|
|
2096
|
-
const containerRef =
|
|
2097
|
-
const widgetIdRef =
|
|
2098
|
-
const onTokenRef =
|
|
2294
|
+
const containerRef = useRef7(null);
|
|
2295
|
+
const widgetIdRef = useRef7(null);
|
|
2296
|
+
const onTokenRef = useRef7(onToken);
|
|
2099
2297
|
onTokenRef.current = onToken;
|
|
2100
|
-
const onErrorRef =
|
|
2298
|
+
const onErrorRef = useRef7(onError);
|
|
2101
2299
|
onErrorRef.current = onError;
|
|
2102
|
-
const registerResetRef =
|
|
2300
|
+
const registerResetRef = useRef7(registerReset);
|
|
2103
2301
|
registerResetRef.current = registerReset;
|
|
2104
2302
|
useEffect7(() => {
|
|
2105
2303
|
let cancelled = false;
|
|
@@ -2155,7 +2353,7 @@ function TurnstileWidget({
|
|
|
2155
2353
|
// src/FilloForm.tsx
|
|
2156
2354
|
import { Fragment as Fragment3, jsx as jsx7, jsxs as jsxs6 } from "react/jsx-runtime";
|
|
2157
2355
|
function themeProps(theme) {
|
|
2158
|
-
const safeTheme = normalizeFormTheme(theme);
|
|
2356
|
+
const safeTheme = resolveThemeAppearance(normalizeFormTheme(theme));
|
|
2159
2357
|
if (!safeTheme) return {};
|
|
2160
2358
|
const style = {};
|
|
2161
2359
|
if (safeTheme.primary) style["--fillo-primary"] = safeTheme.primary;
|
|
@@ -2175,17 +2373,25 @@ function badgeUrl(href) {
|
|
|
2175
2373
|
url.searchParams.set("utm_source", "form_badge");
|
|
2176
2374
|
return url.toString();
|
|
2177
2375
|
}
|
|
2178
|
-
|
|
2179
|
-
|
|
2180
|
-
|
|
2181
|
-
|
|
2182
|
-
|
|
2183
|
-
|
|
2184
|
-
target?.focus?.();
|
|
2185
|
-
};
|
|
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) {
|
|
2186
2382
|
if (typeof requestAnimationFrame === "function") requestAnimationFrame(run);
|
|
2187
2383
|
else setTimeout(run, 0);
|
|
2188
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
|
+
}
|
|
2189
2395
|
var HP_STYLE = {
|
|
2190
2396
|
position: "absolute",
|
|
2191
2397
|
left: "-9999px",
|
|
@@ -2227,6 +2433,7 @@ function FilloForm(props) {
|
|
|
2227
2433
|
accepting: syncedAccepting,
|
|
2228
2434
|
acceptingReason: syncedAcceptingReason,
|
|
2229
2435
|
uploadsAvailable: syncedUploadsAvailable,
|
|
2436
|
+
uploadFileSizeLimitMb: syncedUploadFileSizeLimitMb,
|
|
2230
2437
|
resolvedSchema,
|
|
2231
2438
|
resolvedTheme,
|
|
2232
2439
|
warningCode: syncedWarningCode,
|
|
@@ -2238,7 +2445,7 @@ function FilloForm(props) {
|
|
|
2238
2445
|
});
|
|
2239
2446
|
const renderResolvedSnapshot = Boolean(resolvedSchema && !devChrome);
|
|
2240
2447
|
const inlineSchema = codeForm ? renderResolvedSnapshot ? resolvedSchema : codeForm.schema : form;
|
|
2241
|
-
const onErrorRef =
|
|
2448
|
+
const onErrorRef = useRef8(onError);
|
|
2242
2449
|
useEffect8(() => {
|
|
2243
2450
|
onErrorRef.current = onError;
|
|
2244
2451
|
});
|
|
@@ -2262,6 +2469,7 @@ function FilloForm(props) {
|
|
|
2262
2469
|
accepting: published.accepting,
|
|
2263
2470
|
acceptingReason: published.acceptingReason,
|
|
2264
2471
|
uploadsAvailable: published.uploadsAvailable,
|
|
2472
|
+
uploadFileSizeLimitMb: published.uploadFileSizeLimitMb,
|
|
2265
2473
|
branding: published.branding ?? null,
|
|
2266
2474
|
challenge: published.challenge ?? null
|
|
2267
2475
|
});
|
|
@@ -2401,6 +2609,7 @@ function FilloForm(props) {
|
|
|
2401
2609
|
stagedNotice: codeChangesStaged,
|
|
2402
2610
|
syncErrorNotice: syncError ?? syncNoticeError ?? void 0,
|
|
2403
2611
|
uploadsAvailable: codeForm ? syncedUploadsAvailable : currentLoaded?.uploadsAvailable,
|
|
2612
|
+
uploadFileSizeLimitMb: codeForm ? syncedUploadFileSizeLimitMb : currentLoaded?.uploadFileSizeLimitMb ?? props.uploadFileSizeLimitMb,
|
|
2404
2613
|
syncWarningCode: codeForm ? syncedWarningCode : void 0,
|
|
2405
2614
|
syncWarningUrl: syncedWarningUrl,
|
|
2406
2615
|
challenge
|
|
@@ -2466,17 +2675,17 @@ function ResolvedForm(props) {
|
|
|
2466
2675
|
const instanceId = useId2().replace(/:/g, "");
|
|
2467
2676
|
const devEnv = useIsDevEnv();
|
|
2468
2677
|
const devChrome = !displayOnly && (props.preview === true || devEnv);
|
|
2469
|
-
const hpRef =
|
|
2678
|
+
const hpRef = useRef8(null);
|
|
2470
2679
|
const codeForm = isCodeForm2(props.form) ? props.form : null;
|
|
2471
2680
|
const client = props.client;
|
|
2472
2681
|
const challenge = props.challenge;
|
|
2473
2682
|
const challengeRequired = Boolean(challenge);
|
|
2474
|
-
const challengeTokenRef =
|
|
2683
|
+
const challengeTokenRef = useRef8(void 0);
|
|
2475
2684
|
const [resyncWarning, setResyncWarning] = useState6(null);
|
|
2476
2685
|
const [resolutionFailureCode, setResolutionFailureCode] = useState6(null);
|
|
2477
2686
|
const [challengeToken, setChallengeToken] = useState6(void 0);
|
|
2478
2687
|
const [challengeError, setChallengeError] = useState6(false);
|
|
2479
|
-
const resetChallengeRef =
|
|
2688
|
+
const resetChallengeRef = useRef8(void 0);
|
|
2480
2689
|
const handleChallengeToken = useCallback((token) => {
|
|
2481
2690
|
challengeTokenRef.current = token;
|
|
2482
2691
|
setChallengeToken(token);
|
|
@@ -2494,7 +2703,8 @@ function ResolvedForm(props) {
|
|
|
2494
2703
|
setResyncWarning({
|
|
2495
2704
|
code: result.warningCode,
|
|
2496
2705
|
url: result.warningUrl,
|
|
2497
|
-
uploadsAvailable: result.uploadsAvailable
|
|
2706
|
+
uploadsAvailable: result.uploadsAvailable,
|
|
2707
|
+
uploadFileSizeLimitMb: result.uploadFileSizeLimitMb
|
|
2498
2708
|
});
|
|
2499
2709
|
if (result.status === "draft") {
|
|
2500
2710
|
throw new FilloError2(
|
|
@@ -2581,18 +2791,42 @@ function ResolvedForm(props) {
|
|
|
2581
2791
|
}
|
|
2582
2792
|
});
|
|
2583
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]);
|
|
2584
2816
|
const themed = useMemo5(() => themeProps(theme), [theme]);
|
|
2585
2817
|
const warningCode = resyncWarning ? resyncWarning.code : props.syncWarningCode;
|
|
2586
2818
|
const warningUrl = resyncWarning ? resyncWarning.url : props.syncWarningUrl;
|
|
2587
2819
|
const uploadsAvailable = resyncWarning ? resyncWarning.uploadsAvailable : props.uploadsAvailable;
|
|
2820
|
+
const uploadFileSizeLimitMb = resyncWarning ? resyncWarning.uploadFileSizeLimitMb : props.uploadFileSizeLimitMb;
|
|
2588
2821
|
const chromeValue = useMemo5(
|
|
2589
2822
|
() => ({
|
|
2590
2823
|
devChrome,
|
|
2591
2824
|
uploadsAvailable,
|
|
2825
|
+
uploadFileSizeLimitMb,
|
|
2592
2826
|
warningCode,
|
|
2593
2827
|
warningUrl
|
|
2594
2828
|
}),
|
|
2595
|
-
[devChrome, uploadsAvailable, warningCode, warningUrl]
|
|
2829
|
+
[devChrome, uploadsAvailable, uploadFileSizeLimitMb, warningCode, warningUrl]
|
|
2596
2830
|
);
|
|
2597
2831
|
const settings = schema.settings;
|
|
2598
2832
|
const submitted = api.status === "submitted";
|
|
@@ -2602,19 +2836,23 @@ function ResolvedForm(props) {
|
|
|
2602
2836
|
const url = safeHttpUrl(settings.redirectUrl);
|
|
2603
2837
|
if (url) window.location.assign(url);
|
|
2604
2838
|
}, [liveSubmit, settings.redirectUrl]);
|
|
2605
|
-
const successRef =
|
|
2839
|
+
const successRef = useRef8(null);
|
|
2606
2840
|
useEffect8(() => {
|
|
2607
2841
|
if (liveSubmit) successRef.current?.focus();
|
|
2608
2842
|
}, [liveSubmit]);
|
|
2609
|
-
const blocksRef =
|
|
2610
|
-
const prevPageIndex =
|
|
2843
|
+
const blocksRef = useRef8(null);
|
|
2844
|
+
const prevPageIndex = useRef8(api.pageIndex);
|
|
2611
2845
|
useEffect8(() => {
|
|
2612
2846
|
if (prevPageIndex.current === api.pageIndex) return;
|
|
2613
2847
|
prevPageIndex.current = api.pageIndex;
|
|
2614
|
-
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();
|
|
2615
2854
|
}, [api.pageIndex]);
|
|
2616
2855
|
const appearance = props.appearance;
|
|
2617
|
-
const strings = resolveStrings(props.strings);
|
|
2618
2856
|
if (api.status === "submitted") {
|
|
2619
2857
|
return /* @__PURE__ */ jsxs6(
|
|
2620
2858
|
"div",
|
|
@@ -2638,7 +2876,7 @@ function ResolvedForm(props) {
|
|
|
2638
2876
|
className: slotClass3("fillo-success", appearance, { slot: "success", status: "submitted" }),
|
|
2639
2877
|
"data-fillo": "success",
|
|
2640
2878
|
children: [
|
|
2641
|
-
/* @__PURE__ */ jsx7("div", { className: "fillo-success-mark", "aria-hidden": "true"
|
|
2879
|
+
/* @__PURE__ */ jsx7("div", { className: "fillo-success-mark", "aria-hidden": "true" }),
|
|
2642
2880
|
/* @__PURE__ */ jsx7("h2", { className: "fillo-success-title", children: settings.successTitle ?? strings.successTitle }),
|
|
2643
2881
|
/* @__PURE__ */ jsx7("p", { className: "fillo-success-message", children: api.duplicateSubmission ? strings.alreadyAnswered : settings.successMessage ?? strings.successMessage })
|
|
2644
2882
|
]
|
|
@@ -2661,11 +2899,32 @@ function ResolvedForm(props) {
|
|
|
2661
2899
|
const progressPos = progressSeq.indexOf(api.page.id);
|
|
2662
2900
|
const progressStep = progressPos >= 0 ? progressPos + 1 : Math.min(api.pageIndex + 1, progressTotal);
|
|
2663
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
|
+
}
|
|
2664
2913
|
const rootClass = slotClass3(`fillo-form ${props.className ?? ""}`, appearance, {
|
|
2665
2914
|
slot: "root",
|
|
2666
2915
|
status: api.status
|
|
2667
2916
|
});
|
|
2668
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
|
+
),
|
|
2669
2928
|
devChrome && /* @__PURE__ */ jsx7(
|
|
2670
2929
|
DevChrome,
|
|
2671
2930
|
{
|
|
@@ -2678,12 +2937,11 @@ function ResolvedForm(props) {
|
|
|
2678
2937
|
noClient: !props.client && !props.skipValidation
|
|
2679
2938
|
}
|
|
2680
2939
|
),
|
|
2681
|
-
|
|
2940
|
+
resuming && /* @__PURE__ */ jsxs6(
|
|
2682
2941
|
"div",
|
|
2683
2942
|
{
|
|
2684
2943
|
className: slotClass3("fillo-resume", appearance, { slot: "resume" }),
|
|
2685
2944
|
"data-fillo": "resume",
|
|
2686
|
-
role: "status",
|
|
2687
2945
|
children: [
|
|
2688
2946
|
/* @__PURE__ */ jsx7("span", { className: "fillo-resume-text", children: api.editingPrevious ? strings.editNotice : strings.resumeNotice }),
|
|
2689
2947
|
/* @__PURE__ */ jsx7("button", { type: "button", className: "fillo-resume-clear", onClick: api.resetDraft, children: strings.resumeStartOver })
|
|
@@ -2731,6 +2989,24 @@ function ResolvedForm(props) {
|
|
|
2731
2989
|
)
|
|
2732
2990
|
] }),
|
|
2733
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
|
+
] }),
|
|
2734
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(
|
|
2735
3011
|
BlockRenderer,
|
|
2736
3012
|
{
|
|
@@ -2807,7 +3083,7 @@ function ResolvedForm(props) {
|
|
|
2807
3083
|
] }),
|
|
2808
3084
|
props.poweredBy && /* @__PURE__ */ jsx7(PoweredBy, { label: props.brandLabel, href: props.brandHref })
|
|
2809
3085
|
] });
|
|
2810
|
-
return /* @__PURE__ */ jsx7(FilloContext.Provider, { value: api, children: /* @__PURE__ */ jsx7(FilloChromeContext.Provider, { value: chromeValue, children: /* @__PURE__ */ jsx7(FilloStringsContext.Provider, { value: strings, children: /* @__PURE__ */ jsx7(FilloAppearanceContext.Provider, { value: appearance, children: /* @__PURE__ */ jsx7(FilloInstanceIdContext.Provider, { value: instanceId, children: displayOnly ? (
|
|
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 ? (
|
|
2811
3087
|
// Display-only preview: a plain div (no form element to submit) with
|
|
2812
3088
|
// every control inside a natively disabled fieldset — unfocusable
|
|
2813
3089
|
// and unfillable even without the stylesheet or inert support.
|
|
@@ -2826,20 +3102,20 @@ function ResolvedForm(props) {
|
|
|
2826
3102
|
e.preventDefault();
|
|
2827
3103
|
const formEl = e.currentTarget;
|
|
2828
3104
|
if (api.isLastPage) {
|
|
2829
|
-
void api.submit().then(() =>
|
|
3105
|
+
void api.submit().then(() => focusErrorSummary(formEl)).catch(() => {
|
|
2830
3106
|
});
|
|
2831
3107
|
} else {
|
|
2832
3108
|
api.next();
|
|
2833
|
-
|
|
3109
|
+
focusErrorSummary(formEl);
|
|
2834
3110
|
}
|
|
2835
3111
|
},
|
|
2836
3112
|
children: formBody
|
|
2837
3113
|
}
|
|
2838
|
-
) }) }) }) }) });
|
|
3114
|
+
) }) }) }) }) }) });
|
|
2839
3115
|
}
|
|
2840
3116
|
|
|
2841
3117
|
// src/jsx.tsx
|
|
2842
|
-
import { useRef as
|
|
3118
|
+
import { useRef as useRef9 } from "react";
|
|
2843
3119
|
import {
|
|
2844
3120
|
codeFormFromJsx,
|
|
2845
3121
|
FilloJsxError,
|
|
@@ -2853,7 +3129,7 @@ function JsxForm(props) {
|
|
|
2853
3129
|
const { id, title, description, settings, theme, client, children, ...rest } = props;
|
|
2854
3130
|
const compiled = codeFormFromJsx({ id, title, description, settings, theme }, children);
|
|
2855
3131
|
const json = JSON.stringify(compiled.schema) + JSON.stringify(compiled.theme ?? null);
|
|
2856
|
-
const memo2 =
|
|
3132
|
+
const memo2 = useRef9(null);
|
|
2857
3133
|
if (memo2.current && memo2.current.json !== json && isDevEnv()) {
|
|
2858
3134
|
console.warn(
|
|
2859
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.`
|
|
@@ -2897,6 +3173,7 @@ var Fillo = {
|
|
|
2897
3173
|
Date: B.Date,
|
|
2898
3174
|
FileUpload: B.FileUpload,
|
|
2899
3175
|
Hidden: B.Hidden,
|
|
3176
|
+
Calculated: B.Calculated,
|
|
2900
3177
|
Custom: B.Custom,
|
|
2901
3178
|
Heading: B.Heading,
|
|
2902
3179
|
Paragraph: B.Paragraph,
|
|
@@ -2942,6 +3219,7 @@ function FilloProvider({
|
|
|
2942
3219
|
accepting: syncedAccepting,
|
|
2943
3220
|
acceptingReason: syncedAcceptingReason,
|
|
2944
3221
|
uploadsAvailable,
|
|
3222
|
+
uploadFileSizeLimitMb,
|
|
2945
3223
|
warningCode,
|
|
2946
3224
|
warningUrl
|
|
2947
3225
|
} = useCodeFormSync(form, client, onError, {
|
|
@@ -2961,7 +3239,8 @@ function FilloProvider({
|
|
|
2961
3239
|
setResyncWarning({
|
|
2962
3240
|
code: result.warningCode,
|
|
2963
3241
|
url: result.warningUrl,
|
|
2964
|
-
uploadsAvailable: result.uploadsAvailable
|
|
3242
|
+
uploadsAvailable: result.uploadsAvailable,
|
|
3243
|
+
uploadFileSizeLimitMb: result.uploadFileSizeLimitMb
|
|
2965
3244
|
});
|
|
2966
3245
|
if (result.status === "draft") {
|
|
2967
3246
|
throw new FilloError3(
|
|
@@ -3037,14 +3316,16 @@ function FilloProvider({
|
|
|
3037
3316
|
const freshWarningCode = resyncWarning ? resyncWarning.code : warningCode;
|
|
3038
3317
|
const freshWarningUrl = resyncWarning ? resyncWarning.url : warningUrl;
|
|
3039
3318
|
const freshUploadsAvailable = resyncWarning ? resyncWarning.uploadsAvailable : uploadsAvailable;
|
|
3319
|
+
const freshUploadFileSizeLimitMb = resyncWarning ? resyncWarning.uploadFileSizeLimitMb : uploadFileSizeLimitMb;
|
|
3040
3320
|
const chromeValue = useMemo6(
|
|
3041
3321
|
() => ({
|
|
3042
3322
|
devChrome,
|
|
3043
3323
|
uploadsAvailable: freshUploadsAvailable,
|
|
3324
|
+
uploadFileSizeLimitMb: freshUploadFileSizeLimitMb,
|
|
3044
3325
|
warningCode: freshWarningCode,
|
|
3045
3326
|
warningUrl: freshWarningUrl
|
|
3046
3327
|
}),
|
|
3047
|
-
[devChrome, freshUploadsAvailable, freshWarningCode, freshWarningUrl]
|
|
3328
|
+
[devChrome, freshUploadsAvailable, freshUploadFileSizeLimitMb, freshWarningCode, freshWarningUrl]
|
|
3048
3329
|
);
|
|
3049
3330
|
const resolvedStrings = resolveStrings2(strings);
|
|
3050
3331
|
const canonicalSyncError = syncError && !options.skipValidation ? syncError : null;
|