@usefillo/react 0.12.1 → 0.14.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.js CHANGED
@@ -2,11 +2,11 @@
2
2
 
3
3
  // src/FilloForm.tsx
4
4
  import {
5
- useCallback,
6
- useEffect as useEffect8,
5
+ useCallback as useCallback2,
6
+ useEffect as useEffect9,
7
7
  useId as useId2,
8
- useMemo as useMemo5,
9
- useRef as useRef8,
8
+ useMemo as useMemo6,
9
+ useRef as useRef9,
10
10
  useState as useState6,
11
11
  useSyncExternalStore as useSyncExternalStore2
12
12
  } from "react";
@@ -492,7 +492,171 @@ import {
492
492
  slotClass as slotClass2,
493
493
  visibleFields
494
494
  } from "@usefillo/core";
495
- import { memo, useEffect as useEffect6, useLayoutEffect as useLayoutEffect2, useMemo as useMemo4, useRef as useRef6, useState as useState5 } from "react";
495
+ import { memo, useEffect as useEffect7, useLayoutEffect as useLayoutEffect2, useMemo as useMemo5, useRef as useRef7, useState as useState5 } from "react";
496
+
497
+ // src/group.tsx
498
+ import { visibleGroupChildren } from "@usefillo/core";
499
+ import { useCallback, useContext as useContext3, useEffect as useEffect3, useMemo as useMemo3, useRef as useRef3 } from "react";
500
+ import { jsx as jsx2, jsxs as jsxs2 } from "react/jsx-runtime";
501
+ var EMPTY_INSTANCE = {};
502
+ var FOCUSABLE_SELECTOR = 'input:not(:disabled), select:not(:disabled), textarea:not(:disabled), button:not(:disabled), [tabindex]:not([tabindex="-1"])';
503
+ function firstFieldControl(card) {
504
+ const candidates = card.querySelectorAll(FOCUSABLE_SELECTOR);
505
+ for (const el of candidates) {
506
+ if (!el.classList.contains("fillo-group-remove")) return el;
507
+ }
508
+ return null;
509
+ }
510
+ function materializeInstances(value, count) {
511
+ const base = Array.isArray(value) ? value : [];
512
+ const out = base.slice(0, count);
513
+ while (out.length < count) out.push({});
514
+ return out;
515
+ }
516
+ function GroupInstance({
517
+ group,
518
+ index,
519
+ count,
520
+ item,
521
+ instanceValue,
522
+ api,
523
+ atFloor,
524
+ onPatch,
525
+ onRemove,
526
+ cardRef
527
+ }) {
528
+ const strings = useStrings();
529
+ const groupId = group.id;
530
+ const n = index + 1;
531
+ const label = strings.groupInstanceLabel(item, n, count);
532
+ const { scopedChildren, data } = useMemo3(() => {
533
+ const prefix = `${groupId}.${index}.`;
534
+ const visible = visibleGroupChildren(group, instanceValue);
535
+ const scoped = [];
536
+ const scopedData = {};
537
+ for (const child of visible) {
538
+ const compoundId = prefix + child.id;
539
+ scoped.push({ ...child, id: compoundId });
540
+ scopedData[compoundId] = instanceValue[child.id];
541
+ }
542
+ return { scopedChildren: scoped, data: scopedData };
543
+ }, [group, instanceValue, groupId, index]);
544
+ const setValue = useCallback(
545
+ (fieldId, v) => {
546
+ const prefix = `${groupId}.${index}.`;
547
+ const childId = fieldId.startsWith(prefix) ? fieldId.slice(prefix.length) : fieldId;
548
+ onPatch(index, childId, v);
549
+ },
550
+ [groupId, index, onPatch]
551
+ );
552
+ const childApi = { ...api, data, errors: api.errors, setValue };
553
+ return /* @__PURE__ */ jsxs2("div", { ref: cardRef, className: "fillo-group-instance", role: "group", "aria-label": label, tabIndex: -1, children: [
554
+ /* @__PURE__ */ jsxs2("div", { className: "fillo-group-instance-header", children: [
555
+ /* @__PURE__ */ jsx2("p", { className: "fillo-group-instance-title", children: label }),
556
+ /* @__PURE__ */ jsx2(
557
+ "button",
558
+ {
559
+ type: "button",
560
+ className: "fillo-group-remove",
561
+ "aria-label": strings.groupRemoveLabel(item, n),
562
+ disabled: atFloor,
563
+ title: atFloor ? `At least ${Math.max(group.minInstances ?? 1, 0)} required` : void 0,
564
+ onClick: () => onRemove(index),
565
+ children: "\xD7"
566
+ }
567
+ )
568
+ ] }),
569
+ scopedChildren.map((child) => /* @__PURE__ */ jsx2(BlockRenderer, { block: child, api: childApi }, child.id))
570
+ ] });
571
+ }
572
+ function RepeatingGroup({ field, error, setValue, api, ids: providedIds }) {
573
+ const ids = useFilloFieldIds(field.id, providedIds);
574
+ const strings = useStrings();
575
+ const announce = useFilloAnnounce();
576
+ const group = field;
577
+ const item = group.itemLabel || group.label;
578
+ const liveApi = useContext3(FilloContext) ?? api;
579
+ const value = liveApi.data[field.id];
580
+ const storedInstances = Array.isArray(value) ? value : void 0;
581
+ const floor = Math.max(group.minInstances ?? 1, 0);
582
+ const renderedCount = Math.max(storedInstances?.length ?? 0, floor);
583
+ const atMax = renderedCount >= group.maxInstances;
584
+ const atFloor = renderedCount <= floor;
585
+ const instanceRefs = useRef3([]);
586
+ const addButtonRef = useRef3(null);
587
+ const pendingFocus = useRef3(null);
588
+ useEffect3(() => {
589
+ const pending = pendingFocus.current;
590
+ pendingFocus.current = null;
591
+ if (!pending) return;
592
+ if (pending.target === "add") {
593
+ addButtonRef.current?.focus();
594
+ return;
595
+ }
596
+ const card = instanceRefs.current[pending.index];
597
+ if (!card) return;
598
+ if (pending.target === "instance-card") {
599
+ card.focus();
600
+ return;
601
+ }
602
+ (firstFieldControl(card) ?? card).focus();
603
+ }, [value]);
604
+ function patchInstance(index, childId, v) {
605
+ const instances = materializeInstances(value, renderedCount);
606
+ instances[index] = { ...instances[index] ?? {}, [childId]: v };
607
+ setValue(instances);
608
+ }
609
+ function handleAdd() {
610
+ if (atMax) return;
611
+ const instances = materializeInstances(value, renderedCount);
612
+ instances.push({});
613
+ const n = instances.length;
614
+ pendingFocus.current = { target: "instance-control", index: n - 1 };
615
+ setValue(instances);
616
+ announce(strings.groupInstanceAdded(item, n, n));
617
+ }
618
+ function handleRemove(index) {
619
+ if (atFloor) return;
620
+ const instances = materializeInstances(value, renderedCount);
621
+ instances.splice(index, 1);
622
+ pendingFocus.current = index > 0 ? { target: "instance-card", index: index - 1 } : { target: "add" };
623
+ setValue(instances);
624
+ announce(strings.groupInstanceRemoved(item, instances.length));
625
+ }
626
+ return /* @__PURE__ */ jsx2(FieldShell, { field: group, error, ids, children: /* @__PURE__ */ jsxs2("div", { className: "fillo-group", children: [
627
+ Array.from({ length: renderedCount }, (_, index) => /* @__PURE__ */ jsx2(
628
+ GroupInstance,
629
+ {
630
+ group,
631
+ index,
632
+ count: renderedCount,
633
+ item,
634
+ instanceValue: storedInstances?.[index] ?? EMPTY_INSTANCE,
635
+ api: liveApi,
636
+ atFloor,
637
+ onPatch: patchInstance,
638
+ onRemove: handleRemove,
639
+ cardRef: (el) => {
640
+ instanceRefs.current[index] = el;
641
+ }
642
+ },
643
+ index
644
+ )),
645
+ /* @__PURE__ */ jsx2(
646
+ "button",
647
+ {
648
+ ref: addButtonRef,
649
+ type: "button",
650
+ className: "fillo-group-add",
651
+ disabled: atMax,
652
+ "aria-disabled": atMax ? true : void 0,
653
+ title: atMax ? `Maximum ${group.maxInstances} reached` : void 0,
654
+ onClick: handleAdd,
655
+ children: group.addLabel || strings.groupAdd
656
+ }
657
+ )
658
+ ] }) });
659
+ }
496
660
 
497
661
  // src/phone.tsx
498
662
  import {
@@ -507,8 +671,8 @@ import {
507
671
  positionPhonePopover,
508
672
  toE164
509
673
  } from "@usefillo/core";
510
- import { useEffect as useEffect3, useLayoutEffect, useMemo as useMemo3, useRef as useRef3, useState as useState2 } from "react";
511
- import { jsx as jsx2, jsxs as jsxs2 } from "react/jsx-runtime";
674
+ import { useEffect as useEffect4, useLayoutEffect, useMemo as useMemo4, useRef as useRef4, useState as useState2 } from "react";
675
+ import { jsx as jsx3, jsxs as jsxs3 } from "react/jsx-runtime";
512
676
  function localeCountry() {
513
677
  if (typeof navigator === "undefined") return void 0;
514
678
  const tag = navigator.languages?.[0] || navigator.language || "";
@@ -537,13 +701,13 @@ function PhoneField({ field, value, error, setValue, ids: providedIds }) {
537
701
  const [country, setCountry] = useState2(
538
702
  () => parsePhone(stored).country ?? countryByIso(schema.defaultCountry) ?? PHONE_COUNTRIES[0]
539
703
  );
540
- const touched = useRef3(false);
541
- useEffect3(() => {
704
+ const touched = useRef4(false);
705
+ useEffect4(() => {
542
706
  if (touched.current || !stored) return;
543
707
  const restoredCountry = parsePhone(stored).country;
544
708
  if (restoredCountry) setCountry(restoredCountry);
545
709
  }, [stored]);
546
- useEffect3(() => {
710
+ useEffect4(() => {
547
711
  if (touched.current) return;
548
712
  if (parsePhone(stored).country || countryByIso(schema.defaultCountry)) return;
549
713
  const guess = countryByTimeZone(browserTimeZone()) ?? countryByIso(localeCountry());
@@ -552,8 +716,8 @@ function PhoneField({ field, value, error, setValue, ids: providedIds }) {
552
716
  const [pendingRaw, setPendingRaw] = useState2(null);
553
717
  const national = nationalFor(stored, country);
554
718
  const display = pendingRaw ?? formatNational(country, national);
555
- const inputRef = useRef3(null);
556
- const caretDigits = useRef3(null);
719
+ const inputRef = useRef4(null);
720
+ const caretDigits = useRef4(null);
557
721
  useLayoutEffect(() => {
558
722
  const el = inputRef.current;
559
723
  if (!el || caretDigits.current == null) return;
@@ -600,15 +764,15 @@ function PhoneField({ field, value, error, setValue, ids: providedIds }) {
600
764
  inputRef.current?.focus();
601
765
  }
602
766
  const describedBy = [field.description ? ids.descriptionId : null, error ? ids.errorId : null].filter(Boolean).join(" ") || void 0;
603
- return /* @__PURE__ */ jsxs2("div", { ...slots.wrapperProps(`fillo-field fillo-field--phone${error ? " fillo-field--error" : ""}`), children: [
604
- /* @__PURE__ */ jsxs2("label", { className: slots.label, "data-fillo": "label", id: ids.labelId, htmlFor: ids.inputId, children: [
767
+ return /* @__PURE__ */ jsxs3("div", { ...slots.wrapperProps(`fillo-field fillo-field--phone${error ? " fillo-field--error" : ""}`), children: [
768
+ /* @__PURE__ */ jsxs3("label", { className: slots.label, "data-fillo": "label", id: ids.labelId, htmlFor: ids.inputId, children: [
605
769
  field.label,
606
- !field.required && /* @__PURE__ */ jsx2("span", { className: "fillo-optional", children: strings.optional })
770
+ !field.required && /* @__PURE__ */ jsx3("span", { className: "fillo-optional", children: strings.optional })
607
771
  ] }),
608
- field.description && /* @__PURE__ */ jsx2("p", { className: slots.description, "data-fillo": "fieldDescription", id: ids.descriptionId, children: field.description }),
609
- /* @__PURE__ */ jsxs2("div", { className: "fillo-phone", children: [
610
- /* @__PURE__ */ jsx2(CountrySelect, { country, onPick: pickCountry, labelledBy: ids.labelId }),
611
- /* @__PURE__ */ jsx2(
772
+ field.description && /* @__PURE__ */ jsx3("p", { className: slots.description, "data-fillo": "fieldDescription", id: ids.descriptionId, children: field.description }),
773
+ /* @__PURE__ */ jsxs3("div", { className: "fillo-phone", children: [
774
+ /* @__PURE__ */ jsx3(CountrySelect, { country, onPick: pickCountry, labelledBy: ids.labelId }),
775
+ /* @__PURE__ */ jsx3(
612
776
  "input",
613
777
  {
614
778
  ref: inputRef,
@@ -627,7 +791,7 @@ function PhoneField({ field, value, error, setValue, ids: providedIds }) {
627
791
  }
628
792
  )
629
793
  ] }),
630
- error && /* @__PURE__ */ jsx2("p", { className: slots.error, "data-fillo": "error", id: ids.errorId, children: error })
794
+ error && /* @__PURE__ */ jsx3("p", { className: slots.error, "data-fillo": "error", id: ids.errorId, children: error })
631
795
  ] });
632
796
  }
633
797
  function CountrySelect({
@@ -641,13 +805,13 @@ function CountrySelect({
641
805
  const [query, setQuery] = useState2("");
642
806
  const [active, setActive] = useState2(0);
643
807
  const [placement, setPlacement] = useState2("below");
644
- const rootRef = useRef3(null);
645
- const triggerRef = useRef3(null);
646
- const popoverRef = useRef3(null);
647
- const searchRef = useRef3(null);
648
- const seedRef = useRef3(null);
808
+ const rootRef = useRef4(null);
809
+ const triggerRef = useRef4(null);
810
+ const popoverRef = useRef4(null);
811
+ const searchRef = useRef4(null);
812
+ const seedRef = useRef4(null);
649
813
  const listId = `${labelledBy}-countries`;
650
- const matches = useMemo3(() => {
814
+ const matches = useMemo4(() => {
651
815
  const q = query.trim().toLowerCase();
652
816
  if (!q) return PHONE_PICKER_COUNTRIES;
653
817
  const qDigits = q.replace(/[^\d]/g, "");
@@ -655,7 +819,7 @@ function CountrySelect({
655
819
  (c) => c.name.toLowerCase().includes(q) || c.iso2.toLowerCase() === q || qDigits.length > 0 && c.dialCode.startsWith(qDigits)
656
820
  );
657
821
  }, [query]);
658
- useEffect3(() => {
822
+ useEffect4(() => {
659
823
  if (!open) return;
660
824
  const seed = seedRef.current ?? "";
661
825
  seedRef.current = null;
@@ -664,7 +828,7 @@ function CountrySelect({
664
828
  const id = requestAnimationFrame(() => searchRef.current?.focus());
665
829
  return () => cancelAnimationFrame(id);
666
830
  }, [open, country]);
667
- useEffect3(() => {
831
+ useEffect4(() => {
668
832
  if (!open) return;
669
833
  const onDown = (e) => {
670
834
  if (!rootRef.current?.contains(e.target)) setOpen(false);
@@ -683,12 +847,12 @@ function CountrySelect({
683
847
  window.removeEventListener("scroll", update, true);
684
848
  };
685
849
  }, [open, matches.length]);
686
- useEffect3(() => {
850
+ useEffect4(() => {
687
851
  if (!open) return;
688
852
  document.getElementById(`${listId}-opt-${active}`)?.scrollIntoView({ block: "nearest" });
689
853
  }, [active, open, listId]);
690
854
  const resultsAnnouncement = strings.phoneResultsCount(matches.length);
691
- useEffect3(() => {
855
+ useEffect4(() => {
692
856
  if (!open || !query.trim()) return;
693
857
  const id = setTimeout(() => announce(resultsAnnouncement), 300);
694
858
  return () => clearTimeout(id);
@@ -738,8 +902,8 @@ function CountrySelect({
738
902
  if (!rootRef.current?.contains(document.activeElement)) setOpen(false);
739
903
  });
740
904
  }
741
- return /* @__PURE__ */ jsxs2("div", { className: "fillo-phone-country", ref: rootRef, onBlur: onCompositeBlur, children: [
742
- /* @__PURE__ */ jsxs2(
905
+ return /* @__PURE__ */ jsxs3("div", { className: "fillo-phone-country", ref: rootRef, onBlur: onCompositeBlur, children: [
906
+ /* @__PURE__ */ jsxs3(
743
907
  "button",
744
908
  {
745
909
  ref: triggerRef,
@@ -752,17 +916,17 @@ function CountrySelect({
752
916
  onClick: () => setOpen((o) => !o),
753
917
  onKeyDown: onTriggerKeyDown,
754
918
  children: [
755
- /* @__PURE__ */ jsx2("span", { className: "fillo-phone-flag-emoji", "aria-hidden": "true", children: flagEmoji(country.iso2) }),
756
- /* @__PURE__ */ jsxs2("span", { className: "fillo-phone-dial", children: [
919
+ /* @__PURE__ */ jsx3("span", { className: "fillo-phone-flag-emoji", "aria-hidden": "true", children: flagEmoji(country.iso2) }),
920
+ /* @__PURE__ */ jsxs3("span", { className: "fillo-phone-dial", children: [
757
921
  "+",
758
922
  country.dialCode
759
923
  ] }),
760
- /* @__PURE__ */ jsx2("span", { className: "fillo-phone-caret", "aria-hidden": "true", children: "\u25BE" })
924
+ /* @__PURE__ */ jsx3("span", { className: "fillo-phone-caret", "aria-hidden": "true", children: "\u25BE" })
761
925
  ]
762
926
  }
763
927
  ),
764
- open && /* @__PURE__ */ jsxs2("div", { ref: popoverRef, className: `fillo-phone-popover fillo-phone-popover--${placement}`, children: [
765
- /* @__PURE__ */ jsx2(
928
+ open && /* @__PURE__ */ jsxs3("div", { ref: popoverRef, className: `fillo-phone-popover fillo-phone-popover--${placement}`, children: [
929
+ /* @__PURE__ */ jsx3(
766
930
  "input",
767
931
  {
768
932
  ref: searchRef,
@@ -783,8 +947,8 @@ function CountrySelect({
783
947
  onKeyDown: onSearchKey
784
948
  }
785
949
  ),
786
- /* @__PURE__ */ jsxs2("ul", { className: "fillo-phone-list", role: "listbox", id: listId, "aria-label": "Country", children: [
787
- matches.map((c, i) => /* @__PURE__ */ jsxs2(
950
+ /* @__PURE__ */ jsxs3("ul", { className: "fillo-phone-list", role: "listbox", id: listId, "aria-label": "Country", children: [
951
+ matches.map((c, i) => /* @__PURE__ */ jsxs3(
788
952
  "li",
789
953
  {
790
954
  id: `${listId}-opt-${i}`,
@@ -797,9 +961,9 @@ function CountrySelect({
797
961
  choose(c);
798
962
  },
799
963
  children: [
800
- /* @__PURE__ */ jsx2("span", { className: "fillo-phone-flag-emoji", "aria-hidden": "true", children: flagEmoji(c.iso2) }),
801
- /* @__PURE__ */ jsx2("span", { className: "fillo-phone-option-name", children: c.name }),
802
- /* @__PURE__ */ jsxs2("span", { className: "fillo-phone-option-dial", children: [
964
+ /* @__PURE__ */ jsx3("span", { className: "fillo-phone-flag-emoji", "aria-hidden": "true", children: flagEmoji(c.iso2) }),
965
+ /* @__PURE__ */ jsx3("span", { className: "fillo-phone-option-name", children: c.name }),
966
+ /* @__PURE__ */ jsxs3("span", { className: "fillo-phone-option-dial", children: [
803
967
  "+",
804
968
  c.dialCode
805
969
  ] })
@@ -807,26 +971,26 @@ function CountrySelect({
807
971
  },
808
972
  c.iso2
809
973
  )),
810
- matches.length === 0 && /* @__PURE__ */ jsx2("li", { className: "fillo-phone-empty", role: "option", "aria-disabled": "true", children: "No matches" })
974
+ matches.length === 0 && /* @__PURE__ */ jsx3("li", { className: "fillo-phone-empty", role: "option", "aria-disabled": "true", children: "No matches" })
811
975
  ] })
812
976
  ] })
813
977
  ] });
814
978
  }
815
979
 
816
980
  // src/signature.tsx
817
- import { useEffect as useEffect4, useRef as useRef4, useState as useState3 } from "react";
818
- import { jsx as jsx3, jsxs as jsxs3 } from "react/jsx-runtime";
981
+ import { useEffect as useEffect5, useRef as useRef5, useState as useState3 } from "react";
982
+ import { jsx as jsx4, jsxs as jsxs4 } from "react/jsx-runtime";
819
983
  function SignatureField({ field, value, error, setValue, ids: providedIds }) {
820
984
  const ids = useFilloFieldIds(field.id, providedIds);
821
985
  const slots = useFieldSlots(field, error);
822
986
  const strings = useStrings();
823
987
  const typedInputId = `${ids.inputId}-typed`;
824
- const canvasRef = useRef4(null);
825
- const typedInputRef = useRef4(null);
826
- const drawing = useRef4(false);
988
+ const canvasRef = useRef5(null);
989
+ const typedInputRef = useRef5(null);
990
+ const drawing = useRef5(false);
827
991
  const [hasInk, setHasInk] = useState3(Boolean(value));
828
992
  const [typedName, setTypedName] = useState3("");
829
- useEffect4(() => {
993
+ useEffect5(() => {
830
994
  const canvas = canvasRef.current;
831
995
  if (!canvas) return;
832
996
  const scale = window.devicePixelRatio || 1;
@@ -841,7 +1005,7 @@ function SignatureField({ field, value, error, setValue, ids: providedIds }) {
841
1005
  ctx.lineJoin = "round";
842
1006
  ctx.strokeStyle = getComputedStyle(canvas).color;
843
1007
  }, []);
844
- useEffect4(() => {
1008
+ useEffect5(() => {
845
1009
  const canvas = canvasRef.current;
846
1010
  const ctx = canvas?.getContext("2d");
847
1011
  if (!canvas || !ctx) return;
@@ -922,21 +1086,21 @@ function SignatureField({ field, value, error, setValue, ids: providedIds }) {
922
1086
  typedInputRef.current?.focus();
923
1087
  }
924
1088
  const describedBy = [field.description ? ids.descriptionId : null, error ? ids.errorId : null].filter(Boolean).join(" ") || void 0;
925
- return /* @__PURE__ */ jsxs3("div", { ...slots.wrapperProps(`fillo-field fillo-field--signature${error ? " fillo-field--error" : ""}`), children: [
926
- /* @__PURE__ */ jsxs3("span", { className: slots.label, "data-fillo": "label", id: ids.labelId, children: [
1089
+ return /* @__PURE__ */ jsxs4("div", { ...slots.wrapperProps(`fillo-field fillo-field--signature${error ? " fillo-field--error" : ""}`), children: [
1090
+ /* @__PURE__ */ jsxs4("span", { className: slots.label, "data-fillo": "label", id: ids.labelId, children: [
927
1091
  field.label,
928
- !field.required && /* @__PURE__ */ jsx3("span", { className: "fillo-optional", children: strings.optional })
1092
+ !field.required && /* @__PURE__ */ jsx4("span", { className: "fillo-optional", children: strings.optional })
929
1093
  ] }),
930
- field.description && /* @__PURE__ */ jsx3("p", { className: slots.description, "data-fillo": "fieldDescription", id: ids.descriptionId, children: field.description }),
931
- /* @__PURE__ */ jsxs3(
1094
+ field.description && /* @__PURE__ */ jsx4("p", { className: slots.description, "data-fillo": "fieldDescription", id: ids.descriptionId, children: field.description }),
1095
+ /* @__PURE__ */ jsxs4(
932
1096
  "div",
933
1097
  {
934
1098
  role: "group",
935
1099
  "aria-labelledby": ids.labelId,
936
1100
  "aria-describedby": describedBy,
937
1101
  children: [
938
- /* @__PURE__ */ jsxs3("div", { className: slots.control("fillo-signature"), "data-fillo": "control", children: [
939
- /* @__PURE__ */ jsx3(
1102
+ /* @__PURE__ */ jsxs4("div", { className: slots.control("fillo-signature"), "data-fillo": "control", children: [
1103
+ /* @__PURE__ */ jsx4(
940
1104
  "canvas",
941
1105
  {
942
1106
  ref: canvasRef,
@@ -950,12 +1114,12 @@ function SignatureField({ field, value, error, setValue, ids: providedIds }) {
950
1114
  onPointerLeave: end
951
1115
  }
952
1116
  ),
953
- hasInk && /* @__PURE__ */ jsx3("button", { type: "button", className: "fillo-signature-clear", onClick: clear, children: "Clear" }),
954
- !hasInk && /* @__PURE__ */ jsx3("span", { className: "fillo-signature-hint", "aria-hidden": "true", children: "Sign here" })
1117
+ hasInk && /* @__PURE__ */ jsx4("button", { type: "button", className: "fillo-signature-clear", onClick: clear, children: "Clear" }),
1118
+ !hasInk && /* @__PURE__ */ jsx4("span", { className: "fillo-signature-hint", "aria-hidden": "true", children: "Sign here" })
955
1119
  ] }),
956
- /* @__PURE__ */ jsxs3("div", { className: "fillo-signature-type", children: [
957
- /* @__PURE__ */ jsx3("label", { className: "fillo-signature-type-label", htmlFor: typedInputId, children: "Or type your full name to sign" }),
958
- /* @__PURE__ */ jsx3(
1120
+ /* @__PURE__ */ jsxs4("div", { className: "fillo-signature-type", children: [
1121
+ /* @__PURE__ */ jsx4("label", { className: "fillo-signature-type-label", htmlFor: typedInputId, children: "Or type your full name to sign" }),
1122
+ /* @__PURE__ */ jsx4(
959
1123
  "input",
960
1124
  {
961
1125
  id: typedInputId,
@@ -978,13 +1142,13 @@ function SignatureField({ field, value, error, setValue, ids: providedIds }) {
978
1142
  ]
979
1143
  }
980
1144
  ),
981
- error && /* @__PURE__ */ jsx3("p", { className: slots.error, "data-fillo": "error", id: ids.errorId, children: error })
1145
+ error && /* @__PURE__ */ jsx4("p", { className: slots.error, "data-fillo": "error", id: ids.errorId, children: error })
982
1146
  ] });
983
1147
  }
984
1148
 
985
1149
  // src/upload.tsx
986
- import { useEffect as useEffect5, useRef as useRef5, useState as useState4 } from "react";
987
- import { Fragment as Fragment2, jsx as jsx4, jsxs as jsxs4 } from "react/jsx-runtime";
1150
+ import { useEffect as useEffect6, useRef as useRef6, useState as useState4 } from "react";
1151
+ import { Fragment as Fragment2, jsx as jsx5, jsxs as jsxs5 } from "react/jsx-runtime";
988
1152
  function uploadStatus(inFlight, doneCount, strings) {
989
1153
  const failed = inFlight.filter((f) => f.error).length;
990
1154
  const active = inFlight.length - failed;
@@ -1010,7 +1174,7 @@ function uploadFailureMessage(error, strings) {
1010
1174
  }
1011
1175
  function FileStateIcon({ state }) {
1012
1176
  if (state === "done") {
1013
- return /* @__PURE__ */ jsx4("span", { className: "fillo-file-state fillo-file-state--done", "aria-hidden": "true", children: /* @__PURE__ */ jsxs4(
1177
+ return /* @__PURE__ */ jsx5("span", { className: "fillo-file-state fillo-file-state--done", "aria-hidden": "true", children: /* @__PURE__ */ jsxs5(
1014
1178
  "svg",
1015
1179
  {
1016
1180
  className: "fillo-file-state-icon",
@@ -1021,14 +1185,14 @@ function FileStateIcon({ state }) {
1021
1185
  "aria-hidden": "true",
1022
1186
  focusable: "false",
1023
1187
  children: [
1024
- /* @__PURE__ */ jsx4("path", { d: "M3.75 12a8.25 8.25 0 1 0 16.5 0 8.25 8.25 0 1 0-16.5 0" }),
1025
- /* @__PURE__ */ jsx4("path", { d: "m8.5 12 2.25 2.25 4.75-5" })
1188
+ /* @__PURE__ */ jsx5("path", { d: "M3.75 12a8.25 8.25 0 1 0 16.5 0 8.25 8.25 0 1 0-16.5 0" }),
1189
+ /* @__PURE__ */ jsx5("path", { d: "m8.5 12 2.25 2.25 4.75-5" })
1026
1190
  ]
1027
1191
  }
1028
1192
  ) });
1029
1193
  }
1030
1194
  if (state === "failed") {
1031
- return /* @__PURE__ */ jsx4("span", { className: "fillo-file-state fillo-file-state--failed", "aria-hidden": "true", children: /* @__PURE__ */ jsxs4(
1195
+ return /* @__PURE__ */ jsx5("span", { className: "fillo-file-state fillo-file-state--failed", "aria-hidden": "true", children: /* @__PURE__ */ jsxs5(
1032
1196
  "svg",
1033
1197
  {
1034
1198
  className: "fillo-file-state-icon",
@@ -1039,14 +1203,14 @@ function FileStateIcon({ state }) {
1039
1203
  "aria-hidden": "true",
1040
1204
  focusable: "false",
1041
1205
  children: [
1042
- /* @__PURE__ */ jsx4("path", { d: "M3.75 12a8.25 8.25 0 1 0 16.5 0 8.25 8.25 0 1 0-16.5 0" }),
1043
- /* @__PURE__ */ jsx4("path", { d: "M12 7.75v5.5" }),
1044
- /* @__PURE__ */ jsx4("path", { d: "M12 16.5h.01" })
1206
+ /* @__PURE__ */ jsx5("path", { d: "M3.75 12a8.25 8.25 0 1 0 16.5 0 8.25 8.25 0 1 0-16.5 0" }),
1207
+ /* @__PURE__ */ jsx5("path", { d: "M12 7.75v5.5" }),
1208
+ /* @__PURE__ */ jsx5("path", { d: "M12 16.5h.01" })
1045
1209
  ]
1046
1210
  }
1047
1211
  ) });
1048
1212
  }
1049
- return /* @__PURE__ */ jsx4("span", { className: "fillo-file-state fillo-file-state--uploading", "aria-hidden": "true", children: /* @__PURE__ */ jsxs4(
1213
+ return /* @__PURE__ */ jsx5("span", { className: "fillo-file-state fillo-file-state--uploading", "aria-hidden": "true", children: /* @__PURE__ */ jsxs5(
1050
1214
  "svg",
1051
1215
  {
1052
1216
  className: "fillo-file-state-icon",
@@ -1057,14 +1221,14 @@ function FileStateIcon({ state }) {
1057
1221
  "aria-hidden": "true",
1058
1222
  focusable: "false",
1059
1223
  children: [
1060
- /* @__PURE__ */ jsx4("path", { d: "M7.25 3.75h6.5l3 3v13.5h-9.5z" }),
1061
- /* @__PURE__ */ jsx4("path", { d: "M13.75 3.75v3h3" })
1224
+ /* @__PURE__ */ jsx5("path", { d: "M7.25 3.75h6.5l3 3v13.5h-9.5z" }),
1225
+ /* @__PURE__ */ jsx5("path", { d: "M13.75 3.75v3h3" })
1062
1226
  ]
1063
1227
  }
1064
1228
  ) });
1065
1229
  }
1066
1230
  function CloseIcon() {
1067
- return /* @__PURE__ */ jsx4(
1231
+ return /* @__PURE__ */ jsx5(
1068
1232
  "svg",
1069
1233
  {
1070
1234
  className: "fillo-file-action-icon",
@@ -1074,7 +1238,7 @@ function CloseIcon() {
1074
1238
  fill: "none",
1075
1239
  "aria-hidden": "true",
1076
1240
  focusable: "false",
1077
- children: /* @__PURE__ */ jsx4("path", { d: "m7 7 10 10M17 7 7 17" })
1241
+ children: /* @__PURE__ */ jsx5("path", { d: "m7 7 10 10M17 7 7 17" })
1078
1242
  }
1079
1243
  );
1080
1244
  }
@@ -1091,15 +1255,15 @@ function FileUploadField({
1091
1255
  const strings = useStrings();
1092
1256
  const chrome = useFilloChrome();
1093
1257
  const schema = field;
1094
- const inputRef = useRef5(null);
1258
+ const inputRef = useRef6(null);
1095
1259
  const [inFlight, setInFlight] = useState4([]);
1096
1260
  const [dragOver, setDragOver] = useState4(false);
1097
- const mountedRef = useRef5(true);
1098
- const controllers = useRef5(/* @__PURE__ */ new Map());
1099
- const inFlightCount = useRef5(0);
1261
+ const mountedRef = useRef6(true);
1262
+ const controllers = useRef6(/* @__PURE__ */ new Map());
1263
+ const inFlightCount = useRef6(0);
1100
1264
  const setUploading = api.setUploading;
1101
1265
  const fieldId = field.id;
1102
- useEffect5(() => {
1266
+ useEffect6(() => {
1103
1267
  mountedRef.current = true;
1104
1268
  return () => {
1105
1269
  mountedRef.current = false;
@@ -1108,7 +1272,7 @@ function FileUploadField({
1108
1272
  };
1109
1273
  }, [fieldId, setUploading]);
1110
1274
  const files = Array.isArray(value) ? value : [];
1111
- const filesRef = useRef5(files);
1275
+ const filesRef = useRef6(files);
1112
1276
  filesRef.current = files;
1113
1277
  const maxFiles = schema.maxFiles ?? 1;
1114
1278
  const configuredMaxMb = schema.maxFileSizeMb ?? 500;
@@ -1185,19 +1349,19 @@ function FileUploadField({
1185
1349
  setValue(next);
1186
1350
  }
1187
1351
  const describedBy = [field.description ? ids.descriptionId : null, error ? ids.errorId : null].filter(Boolean).join(" ") || void 0;
1188
- return /* @__PURE__ */ jsxs4(
1352
+ return /* @__PURE__ */ jsxs5(
1189
1353
  "div",
1190
1354
  {
1191
1355
  ...slots.wrapperProps(
1192
1356
  `fillo-field fillo-field--file_upload${error ? " fillo-field--error" : ""}`
1193
1357
  ),
1194
1358
  children: [
1195
- /* @__PURE__ */ jsxs4("label", { className: slots.label, "data-fillo": "label", id: ids.labelId, htmlFor: ids.inputId, children: [
1359
+ /* @__PURE__ */ jsxs5("label", { className: slots.label, "data-fillo": "label", id: ids.labelId, htmlFor: ids.inputId, children: [
1196
1360
  field.label,
1197
- !field.required && /* @__PURE__ */ jsx4("span", { className: "fillo-optional", children: strings.optional })
1361
+ !field.required && /* @__PURE__ */ jsx5("span", { className: "fillo-optional", children: strings.optional })
1198
1362
  ] }),
1199
- field.description && /* @__PURE__ */ jsx4("p", { className: slots.description, "data-fillo": "fieldDescription", id: ids.descriptionId, children: field.description }),
1200
- remaining > 0 && /* @__PURE__ */ jsxs4(
1363
+ field.description && /* @__PURE__ */ jsx5("p", { className: slots.description, "data-fillo": "fieldDescription", id: ids.descriptionId, children: field.description }),
1364
+ remaining > 0 && /* @__PURE__ */ jsxs5(
1201
1365
  "div",
1202
1366
  {
1203
1367
  "data-fillo": "control",
@@ -1229,7 +1393,7 @@ function FileUploadField({
1229
1393
  }
1230
1394
  },
1231
1395
  children: [
1232
- /* @__PURE__ */ jsx4(
1396
+ /* @__PURE__ */ jsx5(
1233
1397
  "input",
1234
1398
  {
1235
1399
  ref: inputRef,
@@ -1242,20 +1406,20 @@ function FileUploadField({
1242
1406
  onChange: (e) => canUpload && handleFiles(e.target.files)
1243
1407
  }
1244
1408
  ),
1245
- canUpload ? /* @__PURE__ */ jsxs4(Fragment2, { children: [
1246
- /* @__PURE__ */ jsx4("span", { className: "fillo-dropzone-title", children: strings.dropzoneTitle(maxFiles > 1) }),
1247
- /* @__PURE__ */ jsx4("span", { className: "fillo-dropzone-hint", children: strings.dropzoneHint(maxMb) })
1248
- ] }) : storageBlocked && chrome?.devChrome ? /* @__PURE__ */ jsxs4("span", { className: "fillo-dropzone-hint", children: [
1409
+ canUpload ? /* @__PURE__ */ jsxs5(Fragment2, { children: [
1410
+ /* @__PURE__ */ jsx5("span", { className: "fillo-dropzone-title", children: strings.dropzoneTitle(maxFiles > 1) }),
1411
+ /* @__PURE__ */ jsx5("span", { className: "fillo-dropzone-hint", children: strings.dropzoneHint(maxMb) })
1412
+ ] }) : storageBlocked && chrome?.devChrome ? /* @__PURE__ */ jsxs5("span", { className: "fillo-dropzone-hint", children: [
1249
1413
  "Connect file storage to enable uploads",
1250
- storageUrl && /* @__PURE__ */ jsxs4(Fragment2, { children: [
1414
+ storageUrl && /* @__PURE__ */ jsxs5(Fragment2, { children: [
1251
1415
  " \u2014 ",
1252
- /* @__PURE__ */ jsx4("a", { href: storageUrl, target: "_blank", rel: "noopener noreferrer", children: storageUrl })
1416
+ /* @__PURE__ */ jsx5("a", { href: storageUrl, target: "_blank", rel: "noopener noreferrer", children: storageUrl })
1253
1417
  ] })
1254
- ] }) : /* @__PURE__ */ jsx4("span", { className: "fillo-dropzone-hint", children: renderOnly ? strings.uploadsRenderOnly : storageBlocked ? strings.uploadsUnavailable : strings.uploadsDisabled })
1418
+ ] }) : /* @__PURE__ */ jsx5("span", { className: "fillo-dropzone-hint", children: renderOnly ? strings.uploadsRenderOnly : storageBlocked ? strings.uploadsUnavailable : strings.uploadsDisabled })
1255
1419
  ]
1256
1420
  }
1257
1421
  ),
1258
- /* @__PURE__ */ jsx4(
1422
+ /* @__PURE__ */ jsx5(
1259
1423
  "div",
1260
1424
  {
1261
1425
  role: "status",
@@ -1265,33 +1429,33 @@ function FileUploadField({
1265
1429
  children: uploadStatus(inFlight, files.length, strings)
1266
1430
  }
1267
1431
  ),
1268
- (inFlight.length > 0 || files.length > 0) && /* @__PURE__ */ jsxs4("ul", { className: "fillo-files", children: [
1269
- files.map((f) => /* @__PURE__ */ jsxs4("li", { className: "fillo-file fillo-file--done", children: [
1270
- /* @__PURE__ */ jsx4(FileStateIcon, { state: "done" }),
1271
- /* @__PURE__ */ jsxs4("span", { className: "fillo-file-content", children: [
1272
- /* @__PURE__ */ jsx4("span", { className: "fillo-file-name", children: f.name }),
1273
- /* @__PURE__ */ jsx4("span", { className: "fillo-file-meta", children: strings.uploadedFile(formatBytes(f.size)) })
1432
+ (inFlight.length > 0 || files.length > 0) && /* @__PURE__ */ jsxs5("ul", { className: "fillo-files", children: [
1433
+ files.map((f) => /* @__PURE__ */ jsxs5("li", { className: "fillo-file fillo-file--done", children: [
1434
+ /* @__PURE__ */ jsx5(FileStateIcon, { state: "done" }),
1435
+ /* @__PURE__ */ jsxs5("span", { className: "fillo-file-content", children: [
1436
+ /* @__PURE__ */ jsx5("span", { className: "fillo-file-name", children: f.name }),
1437
+ /* @__PURE__ */ jsx5("span", { className: "fillo-file-meta", children: strings.uploadedFile(formatBytes(f.size)) })
1274
1438
  ] }),
1275
- /* @__PURE__ */ jsx4("span", { className: "fillo-file-actions", children: /* @__PURE__ */ jsx4(
1439
+ /* @__PURE__ */ jsx5("span", { className: "fillo-file-actions", children: /* @__PURE__ */ jsx5(
1276
1440
  "button",
1277
1441
  {
1278
1442
  type: "button",
1279
1443
  className: "fillo-file-remove",
1280
1444
  "aria-label": `${strings.uploadRemove} ${f.name}`,
1281
1445
  onClick: () => removeFile(f.fileId),
1282
- children: /* @__PURE__ */ jsx4(CloseIcon, {})
1446
+ children: /* @__PURE__ */ jsx5(CloseIcon, {})
1283
1447
  }
1284
1448
  ) })
1285
1449
  ] }, f.fileId)),
1286
- inFlight.map((f) => /* @__PURE__ */ jsxs4("li", { className: `fillo-file${f.error ? " fillo-file--failed" : ""}`, children: [
1287
- /* @__PURE__ */ jsx4(FileStateIcon, { state: f.error ? "failed" : "uploading" }),
1288
- f.error ? /* @__PURE__ */ jsxs4(Fragment2, { children: [
1289
- /* @__PURE__ */ jsxs4("span", { className: "fillo-file-content", children: [
1290
- /* @__PURE__ */ jsx4("span", { className: "fillo-file-name", children: f.name }),
1291
- /* @__PURE__ */ jsx4("span", { className: "fillo-file-error", role: "alert", children: f.error })
1450
+ inFlight.map((f) => /* @__PURE__ */ jsxs5("li", { className: `fillo-file${f.error ? " fillo-file--failed" : ""}`, children: [
1451
+ /* @__PURE__ */ jsx5(FileStateIcon, { state: f.error ? "failed" : "uploading" }),
1452
+ f.error ? /* @__PURE__ */ jsxs5(Fragment2, { children: [
1453
+ /* @__PURE__ */ jsxs5("span", { className: "fillo-file-content", children: [
1454
+ /* @__PURE__ */ jsx5("span", { className: "fillo-file-name", children: f.name }),
1455
+ /* @__PURE__ */ jsx5("span", { className: "fillo-file-error", role: "alert", children: f.error })
1292
1456
  ] }),
1293
- /* @__PURE__ */ jsxs4("span", { className: "fillo-file-actions", children: [
1294
- f.file && !f.tooLarge && remaining > 0 && /* @__PURE__ */ jsx4(
1457
+ /* @__PURE__ */ jsxs5("span", { className: "fillo-file-actions", children: [
1458
+ f.file && !f.tooLarge && remaining > 0 && /* @__PURE__ */ jsx5(
1295
1459
  "button",
1296
1460
  {
1297
1461
  type: "button",
@@ -1306,33 +1470,33 @@ function FileUploadField({
1306
1470
  children: strings.uploadRetry
1307
1471
  }
1308
1472
  ),
1309
- /* @__PURE__ */ jsx4(
1473
+ /* @__PURE__ */ jsx5(
1310
1474
  "button",
1311
1475
  {
1312
1476
  type: "button",
1313
1477
  className: "fillo-file-remove",
1314
1478
  "aria-label": `${strings.uploadDismiss} ${f.name}`,
1315
1479
  onClick: () => setInFlight((prev) => prev.filter((x) => x.key !== f.key)),
1316
- children: /* @__PURE__ */ jsx4(CloseIcon, {})
1480
+ children: /* @__PURE__ */ jsx5(CloseIcon, {})
1317
1481
  }
1318
1482
  )
1319
1483
  ] })
1320
- ] }) : /* @__PURE__ */ jsxs4(Fragment2, { children: [
1321
- /* @__PURE__ */ jsxs4("span", { className: "fillo-file-content", children: [
1322
- /* @__PURE__ */ jsx4("span", { className: "fillo-file-name", children: f.name }),
1323
- /* @__PURE__ */ jsx4("span", { className: "fillo-file-meta", children: strings.uploadingFile(Math.round(f.fraction * 100), formatBytes(f.size)) })
1484
+ ] }) : /* @__PURE__ */ jsxs5(Fragment2, { children: [
1485
+ /* @__PURE__ */ jsxs5("span", { className: "fillo-file-content", children: [
1486
+ /* @__PURE__ */ jsx5("span", { className: "fillo-file-name", children: f.name }),
1487
+ /* @__PURE__ */ jsx5("span", { className: "fillo-file-meta", children: strings.uploadingFile(Math.round(f.fraction * 100), formatBytes(f.size)) })
1324
1488
  ] }),
1325
- /* @__PURE__ */ jsx4("span", { className: "fillo-file-actions", children: /* @__PURE__ */ jsx4(
1489
+ /* @__PURE__ */ jsx5("span", { className: "fillo-file-actions", children: /* @__PURE__ */ jsx5(
1326
1490
  "button",
1327
1491
  {
1328
1492
  type: "button",
1329
1493
  className: "fillo-file-remove",
1330
1494
  "aria-label": `${strings.uploadCancel} ${f.name}`,
1331
1495
  onClick: () => cancelUpload(f.key),
1332
- children: /* @__PURE__ */ jsx4(CloseIcon, {})
1496
+ children: /* @__PURE__ */ jsx5(CloseIcon, {})
1333
1497
  }
1334
1498
  ) }),
1335
- /* @__PURE__ */ jsx4(
1499
+ /* @__PURE__ */ jsx5(
1336
1500
  "span",
1337
1501
  {
1338
1502
  className: "fillo-progress",
@@ -1341,7 +1505,7 @@ function FileUploadField({
1341
1505
  "aria-valuemin": 0,
1342
1506
  "aria-valuemax": 100,
1343
1507
  "aria-valuenow": Math.round(f.fraction * 100),
1344
- children: /* @__PURE__ */ jsx4(
1508
+ children: /* @__PURE__ */ jsx5(
1345
1509
  "span",
1346
1510
  {
1347
1511
  className: "fillo-progress-bar",
@@ -1353,14 +1517,14 @@ function FileUploadField({
1353
1517
  ] })
1354
1518
  ] }, f.key))
1355
1519
  ] }),
1356
- error && /* @__PURE__ */ jsx4("p", { className: slots.error, "data-fillo": "error", id: ids.errorId, children: error })
1520
+ error && /* @__PURE__ */ jsx5("p", { className: slots.error, "data-fillo": "error", id: ids.errorId, children: error })
1357
1521
  ]
1358
1522
  }
1359
1523
  );
1360
1524
  }
1361
1525
 
1362
1526
  // src/fields.tsx
1363
- import { jsx as jsx5, jsxs as jsxs5 } from "react/jsx-runtime";
1527
+ import { jsx as jsx6, jsxs as jsxs6 } from "react/jsx-runtime";
1364
1528
  function fieldAria(field, error, ids, opts = {}) {
1365
1529
  const describedBy = [
1366
1530
  field.description ? ids.descriptionId : null,
@@ -1405,7 +1569,7 @@ function FieldShell({
1405
1569
  const descriptionCls = useSlotClass("fillo-description", { slot: "fieldDescription", ...state });
1406
1570
  const errorCls = useSlotClass("fillo-error", { slot: "error", ...state });
1407
1571
  const strings = useStrings();
1408
- return /* @__PURE__ */ jsxs5(
1572
+ return /* @__PURE__ */ jsxs6(
1409
1573
  "div",
1410
1574
  {
1411
1575
  className: fieldCls,
@@ -1415,13 +1579,13 @@ function FieldShell({
1415
1579
  "data-invalid": error ? "" : void 0,
1416
1580
  "data-required": field.required ? "" : void 0,
1417
1581
  children: [
1418
- /* @__PURE__ */ jsxs5("label", { className: labelCls, "data-fillo": "label", id: ids.labelId, htmlFor: ids.inputId, children: [
1582
+ /* @__PURE__ */ jsxs6("label", { className: labelCls, "data-fillo": "label", id: ids.labelId, htmlFor: ids.inputId, children: [
1419
1583
  field.label,
1420
- !field.required && /* @__PURE__ */ jsx5("span", { className: "fillo-optional", children: strings.optional })
1584
+ !field.required && !(field.kind === "repeating_group" && (field.minInstances ?? 1) > 0) && /* @__PURE__ */ jsx6("span", { className: "fillo-optional", children: strings.optional })
1421
1585
  ] }),
1422
- field.description && /* @__PURE__ */ jsx5("p", { className: descriptionCls, "data-fillo": "fieldDescription", id: ids.descriptionId, children: field.description }),
1586
+ field.description && /* @__PURE__ */ jsx6("p", { className: descriptionCls, "data-fillo": "fieldDescription", id: ids.descriptionId, children: field.description }),
1423
1587
  children,
1424
- error && /* @__PURE__ */ jsx5("p", { className: errorCls, "data-fillo": "error", id: ids.errorId, children: error })
1588
+ error && /* @__PURE__ */ jsx6("p", { className: errorCls, "data-fillo": "error", id: ids.errorId, children: error })
1425
1589
  ]
1426
1590
  }
1427
1591
  );
@@ -1431,7 +1595,7 @@ function TextInput({ field, value, error, setValue, ids: providedIds }) {
1431
1595
  const type = field.kind === "email" ? "email" : field.kind === "url" ? "url" : field.kind === "number" ? "number" : field.kind === "date" ? "date" : "text";
1432
1596
  const autoComplete = field.kind === "email" ? "email" : field.kind === "url" ? "url" : void 0;
1433
1597
  const control = useControl("fillo-input", field, error);
1434
- return /* @__PURE__ */ jsx5(FieldShell, { field, error, ids, children: /* @__PURE__ */ jsx5(
1598
+ return /* @__PURE__ */ jsx6(FieldShell, { field, error, ids, children: /* @__PURE__ */ jsx6(
1435
1599
  "input",
1436
1600
  {
1437
1601
  id: ids.inputId,
@@ -1452,8 +1616,8 @@ function NumberInput({ field, value, error, setValue, api, ids: providedIds }) {
1452
1616
  const ids = useFilloFieldIds(field.id, providedIds);
1453
1617
  const control = useControl("fillo-input", field, error);
1454
1618
  const [draft, setDraft] = useState5(null);
1455
- const inputRef = useRef6(null);
1456
- const pendingCaret = useRef6(null);
1619
+ const inputRef = useRef7(null);
1620
+ const pendingCaret = useRef7(null);
1457
1621
  const [, setRevertTick] = useState5(0);
1458
1622
  useLayoutEffect2(() => {
1459
1623
  if (pendingCaret.current === null) return;
@@ -1466,7 +1630,7 @@ function NumberInput({ field, value, error, setValue, api, ids: providedIds }) {
1466
1630
  const locale = localeForNotation(num.notation);
1467
1631
  const hasAffix = Boolean(num.prefix) || Boolean(num.suffix);
1468
1632
  if (!grouped && !hasAffix) {
1469
- return /* @__PURE__ */ jsx5(
1633
+ return /* @__PURE__ */ jsx6(
1470
1634
  TextInput,
1471
1635
  {
1472
1636
  field,
@@ -1481,7 +1645,7 @@ function NumberInput({ field, value, error, setValue, api, ids: providedIds }) {
1481
1645
  const stored = typeof value === "string" || typeof value === "number" ? String(value) : "";
1482
1646
  const n = Number(stored);
1483
1647
  const display = draft !== null ? draft : grouped && stored !== "" && Number.isFinite(n) ? formatGroupedNumber(n, { locale, decimals: num.decimals }) : stored;
1484
- const input = /* @__PURE__ */ jsx5(
1648
+ const input = /* @__PURE__ */ jsx6(
1485
1649
  "input",
1486
1650
  {
1487
1651
  ref: inputRef,
@@ -1508,16 +1672,16 @@ function NumberInput({ field, value, error, setValue, api, ids: providedIds }) {
1508
1672
  ...fieldAria(field, error, ids)
1509
1673
  }
1510
1674
  );
1511
- return /* @__PURE__ */ jsx5(FieldShell, { field, error, ids, children: hasAffix ? /* @__PURE__ */ jsxs5("div", { className: "fillo-number", children: [
1512
- num.prefix && /* @__PURE__ */ jsx5("span", { className: "fillo-number-prefix", children: num.prefix }),
1675
+ return /* @__PURE__ */ jsx6(FieldShell, { field, error, ids, children: hasAffix ? /* @__PURE__ */ jsxs6("div", { className: "fillo-number", children: [
1676
+ num.prefix && /* @__PURE__ */ jsx6("span", { className: "fillo-number-prefix", children: num.prefix }),
1513
1677
  input,
1514
- num.suffix && /* @__PURE__ */ jsx5("span", { className: "fillo-number-suffix", children: num.suffix })
1678
+ num.suffix && /* @__PURE__ */ jsx6("span", { className: "fillo-number-suffix", children: num.suffix })
1515
1679
  ] }) : input });
1516
1680
  }
1517
1681
  function LongText({ field, value, error, setValue, ids: providedIds }) {
1518
1682
  const ids = useFilloFieldIds(field.id, providedIds);
1519
1683
  const control = useControl("fillo-input fillo-textarea", field, error);
1520
- return /* @__PURE__ */ jsx5(FieldShell, { field, error, ids, children: /* @__PURE__ */ jsx5(
1684
+ return /* @__PURE__ */ jsx6(FieldShell, { field, error, ids, children: /* @__PURE__ */ jsx6(
1521
1685
  "textarea",
1522
1686
  {
1523
1687
  id: ids.inputId,
@@ -1534,7 +1698,7 @@ function LongText({ field, value, error, setValue, ids: providedIds }) {
1534
1698
  function useDisplayOptions(choice) {
1535
1699
  const idsKey = choice.options.map((o) => o.id).join("\0");
1536
1700
  const [order, setOrder] = useState5(null);
1537
- useEffect6(() => {
1701
+ useEffect7(() => {
1538
1702
  if (!choice.shuffleOptions) return;
1539
1703
  const ids = idsKey === "" ? [] : idsKey.split("\0");
1540
1704
  for (let i = ids.length - 1; i > 0; i--) {
@@ -1554,7 +1718,7 @@ var OPTION_ICON_PATHS = {
1554
1718
  };
1555
1719
  function ChoiceIcon({ icon }) {
1556
1720
  if (!icon) return null;
1557
- return /* @__PURE__ */ jsx5(
1721
+ return /* @__PURE__ */ jsx6(
1558
1722
  "svg",
1559
1723
  {
1560
1724
  className: "fillo-option-icon",
@@ -1565,7 +1729,7 @@ function ChoiceIcon({ icon }) {
1565
1729
  strokeLinecap: "round",
1566
1730
  strokeLinejoin: "round",
1567
1731
  "aria-hidden": "true",
1568
- children: /* @__PURE__ */ jsx5("path", { d: OPTION_ICON_PATHS[icon] })
1732
+ children: /* @__PURE__ */ jsx6("path", { d: OPTION_ICON_PATHS[icon] })
1569
1733
  }
1570
1734
  );
1571
1735
  }
@@ -1579,7 +1743,7 @@ function SingleChoice({ field, value, error, setValue, ids: providedIds }) {
1579
1743
  const isOtherValue = typeof value === "string" && value !== "" && !choice.options.some((o) => o.id === value);
1580
1744
  const [otherOn, setOtherOn] = useState5(isOtherValue);
1581
1745
  const otherActive = isOtherValue || value === "" && otherOn;
1582
- return /* @__PURE__ */ jsx5(FieldShell, { field, error, ids, children: /* @__PURE__ */ jsxs5(
1746
+ return /* @__PURE__ */ jsx6(FieldShell, { field, error, ids, children: /* @__PURE__ */ jsxs6(
1583
1747
  "div",
1584
1748
  {
1585
1749
  className: slotClass2("fillo-options", appearance, { slot: "options", kind: field.kind, fieldId: field.id }),
@@ -1588,7 +1752,7 @@ function SingleChoice({ field, value, error, setValue, ids: providedIds }) {
1588
1752
  "aria-labelledby": ids.labelId,
1589
1753
  ...fieldAria(field, error, ids),
1590
1754
  children: [
1591
- options.map((opt) => /* @__PURE__ */ jsxs5(
1755
+ options.map((opt) => /* @__PURE__ */ jsxs6(
1592
1756
  "label",
1593
1757
  {
1594
1758
  "data-option": opt.id,
@@ -1600,7 +1764,7 @@ function SingleChoice({ field, value, error, setValue, ids: providedIds }) {
1600
1764
  value === opt.id
1601
1765
  ),
1602
1766
  children: [
1603
- /* @__PURE__ */ jsx5(
1767
+ /* @__PURE__ */ jsx6(
1604
1768
  "input",
1605
1769
  {
1606
1770
  type: "radio",
@@ -1613,11 +1777,11 @@ function SingleChoice({ field, value, error, setValue, ids: providedIds }) {
1613
1777
  }
1614
1778
  }
1615
1779
  ),
1616
- /* @__PURE__ */ jsx5(ChoiceIcon, { icon: opt.icon }),
1780
+ /* @__PURE__ */ jsx6(ChoiceIcon, { icon: opt.icon }),
1617
1781
  opt.icon && value === opt.id && // Icon-mode hides the native input, so its check state is
1618
1782
  // 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 })
1783
+ /* @__PURE__ */ jsx6("span", { className: "fillo-option-check", "aria-hidden": "true", children: "\u2713" }),
1784
+ /* @__PURE__ */ jsx6("span", { className: slotClass2("fillo-option-label", appearance, { slot: "optionLabel", kind: field.kind, fieldId: field.id }), "data-fillo": "optionLabel", children: opt.label })
1621
1785
  ]
1622
1786
  },
1623
1787
  opt.id
@@ -1627,7 +1791,7 @@ function SingleChoice({ field, value, error, setValue, ids: providedIds }) {
1627
1791
  // option row above. The free-text input below is its own labelable
1628
1792
  // descendant, so it handles its own clicks without re-toggling the
1629
1793
  // radio (dom's proven pattern).
1630
- /* @__PURE__ */ jsxs5(
1794
+ /* @__PURE__ */ jsxs6(
1631
1795
  "label",
1632
1796
  {
1633
1797
  "data-fillo": "option",
@@ -1638,8 +1802,8 @@ function SingleChoice({ field, value, error, setValue, ids: providedIds }) {
1638
1802
  otherActive
1639
1803
  ),
1640
1804
  children: [
1641
- /* @__PURE__ */ jsxs5("span", { className: "fillo-option-main", children: [
1642
- /* @__PURE__ */ jsx5(
1805
+ /* @__PURE__ */ jsxs6("span", { className: "fillo-option-main", children: [
1806
+ /* @__PURE__ */ jsx6(
1643
1807
  "input",
1644
1808
  {
1645
1809
  type: "radio",
@@ -1652,9 +1816,9 @@ function SingleChoice({ field, value, error, setValue, ids: providedIds }) {
1652
1816
  }
1653
1817
  }
1654
1818
  ),
1655
- /* @__PURE__ */ jsx5("span", { className: slotClass2("fillo-option-label", appearance, { slot: "optionLabel", kind: field.kind, fieldId: field.id }), "data-fillo": "optionLabel", children: strings.other })
1819
+ /* @__PURE__ */ jsx6("span", { className: slotClass2("fillo-option-label", appearance, { slot: "optionLabel", kind: field.kind, fieldId: field.id }), "data-fillo": "optionLabel", children: strings.other })
1656
1820
  ] }),
1657
- otherActive && /* @__PURE__ */ jsx5(
1821
+ otherActive && /* @__PURE__ */ jsx6(
1658
1822
  "input",
1659
1823
  {
1660
1824
  type: "text",
@@ -1686,7 +1850,7 @@ function MultiChoice({ field, value, error, setValue, ids: providedIds }) {
1686
1850
  const toggle = (id) => setValue(selected.includes(id) ? selected.filter((v) => v !== id) : [...selected, id]);
1687
1851
  const appearance = useFilloAppearance();
1688
1852
  const optionCls = (base, optionId, isSelected) => slotClass2(base, appearance, { slot: "option", kind: field.kind, fieldId: field.id, optionId, selected: isSelected });
1689
- return /* @__PURE__ */ jsx5(FieldShell, { field, error, ids, children: /* @__PURE__ */ jsxs5(
1853
+ return /* @__PURE__ */ jsx6(FieldShell, { field, error, ids, children: /* @__PURE__ */ jsxs6(
1690
1854
  "div",
1691
1855
  {
1692
1856
  className: slotClass2("fillo-options", appearance, { slot: "options", kind: field.kind, fieldId: field.id }),
@@ -1695,7 +1859,7 @@ function MultiChoice({ field, value, error, setValue, ids: providedIds }) {
1695
1859
  "aria-labelledby": ids.labelId,
1696
1860
  ...fieldAria(field, error, ids, { required: false }),
1697
1861
  children: [
1698
- options.map((opt) => /* @__PURE__ */ jsxs5(
1862
+ options.map((opt) => /* @__PURE__ */ jsxs6(
1699
1863
  "label",
1700
1864
  {
1701
1865
  "data-option": opt.id,
@@ -1707,7 +1871,7 @@ function MultiChoice({ field, value, error, setValue, ids: providedIds }) {
1707
1871
  selected.includes(opt.id)
1708
1872
  ),
1709
1873
  children: [
1710
- /* @__PURE__ */ jsx5(
1874
+ /* @__PURE__ */ jsx6(
1711
1875
  "input",
1712
1876
  {
1713
1877
  type: "checkbox",
@@ -1716,11 +1880,11 @@ function MultiChoice({ field, value, error, setValue, ids: providedIds }) {
1716
1880
  onChange: () => toggle(opt.id)
1717
1881
  }
1718
1882
  ),
1719
- /* @__PURE__ */ jsx5(ChoiceIcon, { icon: opt.icon }),
1883
+ /* @__PURE__ */ jsx6(ChoiceIcon, { icon: opt.icon }),
1720
1884
  opt.icon && selected.includes(opt.id) && // Icon-mode hides the native input, so its check state is
1721
1885
  // otherwise color-only (forfeits forced-colors' own indicator).
1722
- /* @__PURE__ */ jsx5("span", { className: "fillo-option-check", "aria-hidden": "true", children: "\u2713" }),
1723
- /* @__PURE__ */ jsx5("span", { className: slotClass2("fillo-option-label", appearance, { slot: "optionLabel", kind: field.kind, fieldId: field.id }), "data-fillo": "optionLabel", children: opt.label })
1886
+ /* @__PURE__ */ jsx6("span", { className: "fillo-option-check", "aria-hidden": "true", children: "\u2713" }),
1887
+ /* @__PURE__ */ jsx6("span", { className: slotClass2("fillo-option-label", appearance, { slot: "optionLabel", kind: field.kind, fieldId: field.id }), "data-fillo": "optionLabel", children: opt.label })
1724
1888
  ]
1725
1889
  },
1726
1890
  opt.id
@@ -1730,7 +1894,7 @@ function MultiChoice({ field, value, error, setValue, ids: providedIds }) {
1730
1894
  // option row above. The free-text input below is its own labelable
1731
1895
  // descendant, so it handles its own clicks without re-toggling the
1732
1896
  // checkbox (dom's proven pattern).
1733
- /* @__PURE__ */ jsxs5(
1897
+ /* @__PURE__ */ jsxs6(
1734
1898
  "label",
1735
1899
  {
1736
1900
  "data-fillo": "option",
@@ -1741,8 +1905,8 @@ function MultiChoice({ field, value, error, setValue, ids: providedIds }) {
1741
1905
  otherActive
1742
1906
  ),
1743
1907
  children: [
1744
- /* @__PURE__ */ jsxs5("span", { className: "fillo-option-main", children: [
1745
- /* @__PURE__ */ jsx5(
1908
+ /* @__PURE__ */ jsxs6("span", { className: "fillo-option-main", children: [
1909
+ /* @__PURE__ */ jsx6(
1746
1910
  "input",
1747
1911
  {
1748
1912
  type: "checkbox",
@@ -1758,9 +1922,9 @@ function MultiChoice({ field, value, error, setValue, ids: providedIds }) {
1758
1922
  }
1759
1923
  }
1760
1924
  ),
1761
- /* @__PURE__ */ jsx5("span", { className: slotClass2("fillo-option-label", appearance, { slot: "optionLabel", kind: field.kind, fieldId: field.id }), "data-fillo": "optionLabel", children: strings.other })
1925
+ /* @__PURE__ */ jsx6("span", { className: slotClass2("fillo-option-label", appearance, { slot: "optionLabel", kind: field.kind, fieldId: field.id }), "data-fillo": "optionLabel", children: strings.other })
1762
1926
  ] }),
1763
- otherActive && /* @__PURE__ */ jsx5(
1927
+ otherActive && /* @__PURE__ */ jsx6(
1764
1928
  "input",
1765
1929
  {
1766
1930
  type: "text",
@@ -1798,9 +1962,9 @@ function Dropdown({ field, value, error, setValue, ids: providedIds }) {
1798
1962
  const [otherOn, setOtherOn] = useState5(isOtherValue);
1799
1963
  const otherActive = isOtherValue || value === "" && otherOn;
1800
1964
  const control = useControl("fillo-input fillo-select", field, error);
1801
- return /* @__PURE__ */ jsxs5(FieldShell, { field, error, ids, children: [
1802
- /* @__PURE__ */ jsxs5("div", { className: "fillo-select-wrap", children: [
1803
- /* @__PURE__ */ jsxs5(
1965
+ return /* @__PURE__ */ jsxs6(FieldShell, { field, error, ids, children: [
1966
+ /* @__PURE__ */ jsxs6("div", { className: "fillo-select-wrap", children: [
1967
+ /* @__PURE__ */ jsxs6(
1804
1968
  "select",
1805
1969
  {
1806
1970
  id: ids.inputId,
@@ -1817,15 +1981,15 @@ function Dropdown({ field, value, error, setValue, ids: providedIds }) {
1817
1981
  }
1818
1982
  },
1819
1983
  children: [
1820
- /* @__PURE__ */ jsx5("option", { value: "", children: field.placeholder ?? strings.choosePlaceholder }),
1821
- options.map((opt) => /* @__PURE__ */ jsx5("option", { value: opt.id, children: opt.label }, opt.id)),
1822
- choice.allowOther && /* @__PURE__ */ jsx5("option", { value: otherValue, children: `${strings.other}\u2026` })
1984
+ /* @__PURE__ */ jsx6("option", { value: "", children: field.placeholder ?? strings.choosePlaceholder }),
1985
+ options.map((opt) => /* @__PURE__ */ jsx6("option", { value: opt.id, children: opt.label }, opt.id)),
1986
+ choice.allowOther && /* @__PURE__ */ jsx6("option", { value: otherValue, children: `${strings.other}\u2026` })
1823
1987
  ]
1824
1988
  }
1825
1989
  ),
1826
- /* @__PURE__ */ jsx5("span", { className: "fillo-select-icon", "aria-hidden": "true" })
1990
+ /* @__PURE__ */ jsx6("span", { className: "fillo-select-icon", "aria-hidden": "true" })
1827
1991
  ] }),
1828
- otherActive && /* @__PURE__ */ jsx5(
1992
+ otherActive && /* @__PURE__ */ jsx6(
1829
1993
  "input",
1830
1994
  {
1831
1995
  type: "text",
@@ -1846,19 +2010,19 @@ function Checkbox({ field, value, error, setValue, ids: providedIds }) {
1846
2010
  const slots = useFieldSlots(field, error);
1847
2011
  const checkbox = field;
1848
2012
  if (checkbox.appearance === "toggle") {
1849
- return /* @__PURE__ */ jsxs5(
2013
+ return /* @__PURE__ */ jsxs6(
1850
2014
  "div",
1851
2015
  {
1852
2016
  ...slots.wrapperProps(
1853
2017
  `fillo-field fillo-field--checkbox fillo-field--toggle${error ? " fillo-field--error" : ""}`
1854
2018
  ),
1855
2019
  children: [
1856
- /* @__PURE__ */ jsxs5("label", { className: "fillo-toggle", "data-fillo": "option", "data-checked": value === true ? "" : void 0, children: [
1857
- /* @__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: [
2020
+ /* @__PURE__ */ jsxs6("label", { className: "fillo-toggle", "data-fillo": "option", "data-checked": value === true ? "" : void 0, children: [
2021
+ /* @__PURE__ */ jsx6("span", { className: "fillo-toggle-copy", children: /* @__PURE__ */ jsxs6("span", { className: slotClass2("fillo-option-label", appearance, { slot: "optionLabel", kind: field.kind, fieldId: field.id }), "data-fillo": "optionLabel", children: [
1858
2022
  field.label,
1859
- !field.required && /* @__PURE__ */ jsx5("span", { className: "fillo-optional", children: strings.optional })
2023
+ !field.required && /* @__PURE__ */ jsx6("span", { className: "fillo-optional", children: strings.optional })
1860
2024
  ] }) }),
1861
- /* @__PURE__ */ jsx5(
2025
+ /* @__PURE__ */ jsx6(
1862
2026
  "input",
1863
2027
  {
1864
2028
  id: ids.inputId,
@@ -1869,23 +2033,23 @@ function Checkbox({ field, value, error, setValue, ids: providedIds }) {
1869
2033
  ...fieldAria(field, error, ids)
1870
2034
  }
1871
2035
  ),
1872
- /* @__PURE__ */ jsx5("span", { className: "fillo-toggle-track", "aria-hidden": "true", children: /* @__PURE__ */ jsx5("span", { className: "fillo-toggle-thumb" }) })
2036
+ /* @__PURE__ */ jsx6("span", { className: "fillo-toggle-track", "aria-hidden": "true", children: /* @__PURE__ */ jsx6("span", { className: "fillo-toggle-thumb" }) })
1873
2037
  ] }),
1874
- field.description && /* @__PURE__ */ jsx5("p", { className: slots.description, "data-fillo": "fieldDescription", id: ids.descriptionId, children: field.description }),
1875
- error && /* @__PURE__ */ jsx5("p", { className: slots.error, "data-fillo": "error", id: ids.errorId, children: error })
2038
+ field.description && /* @__PURE__ */ jsx6("p", { className: slots.description, "data-fillo": "fieldDescription", id: ids.descriptionId, children: field.description }),
2039
+ error && /* @__PURE__ */ jsx6("p", { className: slots.error, "data-fillo": "error", id: ids.errorId, children: error })
1876
2040
  ]
1877
2041
  }
1878
2042
  );
1879
2043
  }
1880
- return /* @__PURE__ */ jsxs5(
2044
+ return /* @__PURE__ */ jsxs6(
1881
2045
  "div",
1882
2046
  {
1883
2047
  ...slots.wrapperProps(
1884
2048
  `fillo-field fillo-field--checkbox${error ? " fillo-field--error" : ""}`
1885
2049
  ),
1886
2050
  children: [
1887
- /* @__PURE__ */ jsxs5("label", { className: "fillo-option", "data-fillo": "option", "data-checked": value === true ? "" : void 0, children: [
1888
- /* @__PURE__ */ jsx5(
2051
+ /* @__PURE__ */ jsxs6("label", { className: "fillo-option", "data-fillo": "option", "data-checked": value === true ? "" : void 0, children: [
2052
+ /* @__PURE__ */ jsx6(
1889
2053
  "input",
1890
2054
  {
1891
2055
  id: ids.inputId,
@@ -1896,13 +2060,13 @@ function Checkbox({ field, value, error, setValue, ids: providedIds }) {
1896
2060
  ...fieldAria(field, error, ids)
1897
2061
  }
1898
2062
  ),
1899
- /* @__PURE__ */ jsxs5("span", { className: slotClass2("fillo-option-label", appearance, { slot: "optionLabel", kind: field.kind, fieldId: field.id }), "data-fillo": "optionLabel", children: [
2063
+ /* @__PURE__ */ jsxs6("span", { className: slotClass2("fillo-option-label", appearance, { slot: "optionLabel", kind: field.kind, fieldId: field.id }), "data-fillo": "optionLabel", children: [
1900
2064
  field.label,
1901
- !field.required && /* @__PURE__ */ jsx5("span", { className: "fillo-optional", children: strings.optional })
2065
+ !field.required && /* @__PURE__ */ jsx6("span", { className: "fillo-optional", children: strings.optional })
1902
2066
  ] })
1903
2067
  ] }),
1904
- field.description && /* @__PURE__ */ jsx5("p", { className: slots.description, "data-fillo": "fieldDescription", id: ids.descriptionId, children: field.description }),
1905
- error && /* @__PURE__ */ jsx5("p", { className: slots.error, "data-fillo": "error", id: ids.errorId, children: error })
2068
+ field.description && /* @__PURE__ */ jsx6("p", { className: slots.description, "data-fillo": "fieldDescription", id: ids.descriptionId, children: field.description }),
2069
+ error && /* @__PURE__ */ jsx6("p", { className: slots.error, "data-fillo": "error", id: ids.errorId, children: error })
1906
2070
  ]
1907
2071
  }
1908
2072
  );
@@ -1934,7 +2098,7 @@ function Rating({ field, value, error, setValue, ids: providedIds }) {
1934
2098
  const tabbable = rovingIndex(steps, selected);
1935
2099
  const appearance = useFilloAppearance();
1936
2100
  const control = useControl("fillo-rating", field, error);
1937
- return /* @__PURE__ */ jsx5(FieldShell, { field, error, ids, children: /* @__PURE__ */ jsx5(
2101
+ return /* @__PURE__ */ jsx6(FieldShell, { field, error, ids, children: /* @__PURE__ */ jsx6(
1938
2102
  "div",
1939
2103
  {
1940
2104
  id: ids.inputId,
@@ -1943,7 +2107,7 @@ function Rating({ field, value, error, setValue, ids: providedIds }) {
1943
2107
  "aria-labelledby": ids.labelId,
1944
2108
  ...fieldAria(field, error, ids),
1945
2109
  onKeyDown: (e) => radiogroupKeyDown(e, steps, selected, (n) => setValue(n)),
1946
- children: steps.map((n, i) => /* @__PURE__ */ jsx5(
2110
+ children: steps.map((n, i) => /* @__PURE__ */ jsx6(
1947
2111
  "button",
1948
2112
  {
1949
2113
  type: "button",
@@ -1989,8 +2153,8 @@ function LinearScale({ field, value, error, setValue, ids: providedIds }) {
1989
2153
  ].filter(Boolean).join(" ") || void 0;
1990
2154
  const appearance = useFilloAppearance();
1991
2155
  const control = useControl("fillo-scale", field, error);
1992
- return /* @__PURE__ */ jsxs5(FieldShell, { field, error, ids, children: [
1993
- /* @__PURE__ */ jsx5(
2156
+ return /* @__PURE__ */ jsxs6(FieldShell, { field, error, ids, children: [
2157
+ /* @__PURE__ */ jsx6(
1994
2158
  "div",
1995
2159
  {
1996
2160
  id: ids.inputId,
@@ -2001,7 +2165,7 @@ function LinearScale({ field, value, error, setValue, ids: providedIds }) {
2001
2165
  "aria-required": field.required ? true : void 0,
2002
2166
  "aria-describedby": describedBy,
2003
2167
  onKeyDown: (e) => radiogroupKeyDown(e, steps, current, (n) => setValue(n)),
2004
- children: steps.map((n, i) => /* @__PURE__ */ jsx5(
2168
+ children: steps.map((n, i) => /* @__PURE__ */ jsx6(
2005
2169
  "button",
2006
2170
  {
2007
2171
  type: "button",
@@ -2027,9 +2191,9 @@ function LinearScale({ field, value, error, setValue, ids: providedIds }) {
2027
2191
  ))
2028
2192
  }
2029
2193
  ),
2030
- hasLabels && /* @__PURE__ */ jsxs5("div", { className: "fillo-scale-labels", children: [
2031
- /* @__PURE__ */ jsx5("span", { id: minLabelId, children: scale.minLabel }),
2032
- /* @__PURE__ */ jsx5("span", { id: maxLabelId, children: scale.maxLabel })
2194
+ hasLabels && /* @__PURE__ */ jsxs6("div", { className: "fillo-scale-labels", children: [
2195
+ /* @__PURE__ */ jsx6("span", { id: minLabelId, children: scale.minLabel }),
2196
+ /* @__PURE__ */ jsx6("span", { id: maxLabelId, children: scale.maxLabel })
2033
2197
  ] })
2034
2198
  ] });
2035
2199
  }
@@ -2043,8 +2207,8 @@ function Ranking({ field, value, error, setValue, ids: providedIds }) {
2043
2207
  ...answered.filter((id) => ranking.options.some((o) => o.id === id)),
2044
2208
  ...ranking.options.map((o) => o.id).filter((id) => !answered.includes(id))
2045
2209
  ];
2046
- const pressedRef = useRef6(null);
2047
- useEffect6(() => {
2210
+ const pressedRef = useRef7(null);
2211
+ useEffect7(() => {
2048
2212
  const pressed = pressedRef.current;
2049
2213
  pressedRef.current = null;
2050
2214
  if (!pressed || !pressed.disabled) return;
@@ -2064,21 +2228,21 @@ function Ranking({ field, value, error, setValue, ids: providedIds }) {
2064
2228
  const moved = ranking.options.find((o) => o.id === id);
2065
2229
  if (moved) announce(strings.rankingPosition(moved.label, target + 1, order.length));
2066
2230
  }
2067
- return /* @__PURE__ */ jsx5(FieldShell, { field, error, ids, children: /* @__PURE__ */ jsx5(
2231
+ return /* @__PURE__ */ jsx6(FieldShell, { field, error, ids, children: /* @__PURE__ */ jsx6(
2068
2232
  "div",
2069
2233
  {
2070
2234
  id: ids.inputId,
2071
2235
  role: "group",
2072
2236
  "aria-labelledby": ids.labelId,
2073
2237
  ...fieldAria(field, error, ids, { required: false }),
2074
- children: /* @__PURE__ */ jsx5("ol", { className: "fillo-ranking", children: order.map((id, index) => {
2238
+ children: /* @__PURE__ */ jsx6("ol", { className: "fillo-ranking", children: order.map((id, index) => {
2075
2239
  const option = ranking.options.find((o) => o.id === id);
2076
2240
  if (!option) return null;
2077
- return /* @__PURE__ */ jsxs5("li", { className: "fillo-ranking-item", children: [
2078
- /* @__PURE__ */ jsx5("span", { className: "fillo-ranking-index", children: index + 1 }),
2079
- /* @__PURE__ */ jsx5("span", { className: "fillo-ranking-label", children: option.label }),
2080
- /* @__PURE__ */ jsxs5("span", { className: "fillo-ranking-controls", children: [
2081
- /* @__PURE__ */ jsx5(
2241
+ return /* @__PURE__ */ jsxs6("li", { className: "fillo-ranking-item", children: [
2242
+ /* @__PURE__ */ jsx6("span", { className: "fillo-ranking-index", children: index + 1 }),
2243
+ /* @__PURE__ */ jsx6("span", { className: "fillo-ranking-label", children: option.label }),
2244
+ /* @__PURE__ */ jsxs6("span", { className: "fillo-ranking-controls", children: [
2245
+ /* @__PURE__ */ jsx6(
2082
2246
  "button",
2083
2247
  {
2084
2248
  type: "button",
@@ -2089,7 +2253,7 @@ function Ranking({ field, value, error, setValue, ids: providedIds }) {
2089
2253
  children: "\u2191"
2090
2254
  }
2091
2255
  ),
2092
- /* @__PURE__ */ jsx5(
2256
+ /* @__PURE__ */ jsx6(
2093
2257
  "button",
2094
2258
  {
2095
2259
  type: "button",
@@ -2110,7 +2274,7 @@ function Matrix({ field, value, error, setValue, ids: providedIds }) {
2110
2274
  const ids = useFilloFieldIds(field.id, providedIds);
2111
2275
  const matrix = field;
2112
2276
  const answers = value && typeof value === "object" && !Array.isArray(value) ? value : {};
2113
- return /* @__PURE__ */ jsx5(FieldShell, { field, error, ids, children: /* @__PURE__ */ jsx5(
2277
+ return /* @__PURE__ */ jsx6(FieldShell, { field, error, ids, children: /* @__PURE__ */ jsx6(
2114
2278
  "div",
2115
2279
  {
2116
2280
  id: ids.inputId,
@@ -2118,12 +2282,12 @@ function Matrix({ field, value, error, setValue, ids: providedIds }) {
2118
2282
  role: "group",
2119
2283
  "aria-labelledby": ids.labelId,
2120
2284
  ...fieldAria(field, error, ids, { required: false }),
2121
- children: /* @__PURE__ */ jsxs5("table", { className: "fillo-matrix", children: [
2122
- /* @__PURE__ */ jsx5("thead", { children: /* @__PURE__ */ jsxs5("tr", { children: [
2123
- /* @__PURE__ */ jsx5("td", {}),
2124
- matrix.columns.map((col) => /* @__PURE__ */ jsx5("th", { scope: "col", children: col.label }, col.id))
2285
+ children: /* @__PURE__ */ jsxs6("table", { className: "fillo-matrix", children: [
2286
+ /* @__PURE__ */ jsx6("thead", { children: /* @__PURE__ */ jsxs6("tr", { children: [
2287
+ /* @__PURE__ */ jsx6("td", {}),
2288
+ matrix.columns.map((col) => /* @__PURE__ */ jsx6("th", { scope: "col", children: col.label }, col.id))
2125
2289
  ] }) }),
2126
- /* @__PURE__ */ jsx5("tbody", { children: matrix.rows.map((row) => {
2290
+ /* @__PURE__ */ jsx6("tbody", { children: matrix.rows.map((row) => {
2127
2291
  const rowLabelId = `${ids.inputId}-${row.id}`;
2128
2292
  const answered = answers[row.id] !== void 0;
2129
2293
  const rowInvalid = Boolean(error) && field.required && !answered;
@@ -2134,17 +2298,17 @@ function Matrix({ field, value, error, setValue, ids: providedIds }) {
2134
2298
  // radiogroup lives on an inner element that aria-owns the radios
2135
2299
  // scattered across the cells (they can't be DOM-nested inside a
2136
2300
  // single element within a table row).
2137
- /* @__PURE__ */ jsxs5("tr", { children: [
2138
- /* @__PURE__ */ jsx5("th", { scope: "row", children: /* @__PURE__ */ jsx5(
2301
+ /* @__PURE__ */ jsxs6("tr", { children: [
2302
+ /* @__PURE__ */ jsx6("th", { scope: "row", children: /* @__PURE__ */ jsx6(
2139
2303
  "div",
2140
2304
  {
2141
2305
  role: "radiogroup",
2142
2306
  "aria-labelledby": `${ids.labelId} ${rowLabelId}`,
2143
2307
  "aria-owns": matrix.columns.map(radioId).join(" "),
2144
- children: /* @__PURE__ */ jsx5("span", { id: rowLabelId, children: row.label })
2308
+ children: /* @__PURE__ */ jsx6("span", { id: rowLabelId, children: row.label })
2145
2309
  }
2146
2310
  ) }),
2147
- matrix.columns.map((col) => /* @__PURE__ */ jsx5("td", { "data-label": col.label, children: /* @__PURE__ */ jsx5("label", { className: "fillo-matrix-cell", children: /* @__PURE__ */ jsx5(
2311
+ matrix.columns.map((col) => /* @__PURE__ */ jsx6("td", { "data-label": col.label, children: /* @__PURE__ */ jsx6("label", { className: "fillo-matrix-cell", children: /* @__PURE__ */ jsx6(
2148
2312
  "input",
2149
2313
  {
2150
2314
  id: radioId(col),
@@ -2178,7 +2342,7 @@ function Calculated({ field, value, ids: providedIds }) {
2178
2342
  const labelCls = useSlotClass("fillo-label", { slot: "label", ...state });
2179
2343
  const descriptionCls = useSlotClass("fillo-description", { slot: "fieldDescription", ...state });
2180
2344
  const answered = value !== void 0 && value !== null && value !== "";
2181
- return /* @__PURE__ */ jsxs5(
2345
+ return /* @__PURE__ */ jsxs6(
2182
2346
  "div",
2183
2347
  {
2184
2348
  className: rootCls,
@@ -2186,9 +2350,9 @@ function Calculated({ field, value, ids: providedIds }) {
2186
2350
  "data-field": field.id,
2187
2351
  "data-kind": field.kind,
2188
2352
  children: [
2189
- /* @__PURE__ */ jsx5("label", { className: labelCls, "data-fillo": "label", id: ids.labelId, htmlFor: ids.inputId, children: field.label }),
2190
- field.description && /* @__PURE__ */ jsx5("p", { className: descriptionCls, "data-fillo": "fieldDescription", id: ids.descriptionId, children: field.description }),
2191
- /* @__PURE__ */ jsx5(
2353
+ /* @__PURE__ */ jsx6("label", { className: labelCls, "data-fillo": "label", id: ids.labelId, htmlFor: ids.inputId, children: field.label }),
2354
+ field.description && /* @__PURE__ */ jsx6("p", { className: descriptionCls, "data-fillo": "fieldDescription", id: ids.descriptionId, children: field.description }),
2355
+ /* @__PURE__ */ jsx6(
2192
2356
  "output",
2193
2357
  {
2194
2358
  id: ids.inputId,
@@ -2220,16 +2384,17 @@ var DEFAULT_COMPONENTS = {
2220
2384
  signature: SignatureField,
2221
2385
  file_upload: FileUploadField,
2222
2386
  hidden: Hidden,
2223
- calculated: Calculated
2387
+ calculated: Calculated,
2388
+ repeating_group: RepeatingGroup
2224
2389
  };
2225
2390
  function ContentRenderer({ block }) {
2226
2391
  switch (block.kind) {
2227
2392
  case "heading":
2228
- return /* @__PURE__ */ jsx5("h3", { className: "fillo-heading", children: block.text });
2393
+ return /* @__PURE__ */ jsx6("h3", { className: "fillo-heading", children: block.text });
2229
2394
  case "paragraph":
2230
- return /* @__PURE__ */ jsx5("p", { className: "fillo-paragraph", children: block.text });
2395
+ return /* @__PURE__ */ jsx6("p", { className: "fillo-paragraph", children: block.text });
2231
2396
  case "divider":
2232
- return /* @__PURE__ */ jsx5("hr", { className: "fillo-divider" });
2397
+ return /* @__PURE__ */ jsx6("hr", { className: "fillo-divider" });
2233
2398
  }
2234
2399
  }
2235
2400
  function blockHasPiping(block) {
@@ -2268,7 +2433,7 @@ function BlockRendererInner({ block, api, components, customComponents }) {
2268
2433
  block = pipeBlock(block, api.data, api.form);
2269
2434
  const ids = useFilloFieldIds(block.id);
2270
2435
  const strings = useStrings();
2271
- if (!isField(block)) return /* @__PURE__ */ jsx5(ContentRenderer, { block });
2436
+ if (!isField(block)) return /* @__PURE__ */ jsx6(ContentRenderer, { block });
2272
2437
  let Component;
2273
2438
  if (block.kind === "custom") {
2274
2439
  Component = customComponents?.[block.component] ?? components?.custom;
@@ -2286,7 +2451,7 @@ function BlockRendererInner({ block, api, components, customComponents }) {
2286
2451
  }
2287
2452
  const rawError = api.errors[block.id];
2288
2453
  const error = rawError === REQUIRED_FIELD_MESSAGE ? requiredFieldMessage(block, strings) : rawError;
2289
- return /* @__PURE__ */ jsx5(
2454
+ return /* @__PURE__ */ jsx6(
2290
2455
  Component,
2291
2456
  {
2292
2457
  field: block,
@@ -2316,14 +2481,14 @@ function FormField({
2316
2481
  customComponents
2317
2482
  }) {
2318
2483
  const api = useFillo();
2319
- const visibleIds = useMemo4(
2484
+ const visibleIds = useMemo5(
2320
2485
  () => new Set(visibleFields(api.form, api.data).map((f) => f.id)),
2321
2486
  [api.form, api.data]
2322
2487
  );
2323
2488
  const block = api.form.pages.flatMap((p) => p.blocks).find((b) => b.id === id);
2324
2489
  if (!block) return null;
2325
2490
  if (isField(block) && !visibleIds.has(block.id)) return null;
2326
- return /* @__PURE__ */ jsx5(
2491
+ return /* @__PURE__ */ jsx6(
2327
2492
  BlockRenderer,
2328
2493
  {
2329
2494
  block,
@@ -2335,8 +2500,8 @@ function FormField({
2335
2500
  }
2336
2501
 
2337
2502
  // src/turnstile.tsx
2338
- import { useEffect as useEffect7, useRef as useRef7 } from "react";
2339
- import { jsx as jsx6 } from "react/jsx-runtime";
2503
+ import { useEffect as useEffect8, useRef as useRef8 } from "react";
2504
+ import { jsx as jsx7 } from "react/jsx-runtime";
2340
2505
  var SCRIPT_SRC = "https://challenges.cloudflare.com/turnstile/v0/api.js?render=explicit";
2341
2506
  var SCRIPT_ID = "fillo-turnstile-script";
2342
2507
  var scriptPromise = null;
@@ -2394,15 +2559,15 @@ function TurnstileWidget({
2394
2559
  registerReset,
2395
2560
  className
2396
2561
  }) {
2397
- const containerRef = useRef7(null);
2398
- const widgetIdRef = useRef7(null);
2399
- const onTokenRef = useRef7(onToken);
2562
+ const containerRef = useRef8(null);
2563
+ const widgetIdRef = useRef8(null);
2564
+ const onTokenRef = useRef8(onToken);
2400
2565
  onTokenRef.current = onToken;
2401
- const onErrorRef = useRef7(onError);
2566
+ const onErrorRef = useRef8(onError);
2402
2567
  onErrorRef.current = onError;
2403
- const registerResetRef = useRef7(registerReset);
2568
+ const registerResetRef = useRef8(registerReset);
2404
2569
  registerResetRef.current = registerReset;
2405
- useEffect7(() => {
2570
+ useEffect8(() => {
2406
2571
  let cancelled = false;
2407
2572
  loadTurnstileScript().then(() => {
2408
2573
  if (cancelled || !containerRef.current) return;
@@ -2443,7 +2608,7 @@ function TurnstileWidget({
2443
2608
  onTokenRef.current(void 0);
2444
2609
  };
2445
2610
  }, [siteKey]);
2446
- return /* @__PURE__ */ jsx6(
2611
+ return /* @__PURE__ */ jsx7(
2447
2612
  "div",
2448
2613
  {
2449
2614
  ref: containerRef,
@@ -2454,7 +2619,7 @@ function TurnstileWidget({
2454
2619
  }
2455
2620
 
2456
2621
  // src/FilloForm.tsx
2457
- import { Fragment as Fragment3, jsx as jsx7, jsxs as jsxs6 } from "react/jsx-runtime";
2622
+ import { Fragment as Fragment3, jsx as jsx8, jsxs as jsxs7 } from "react/jsx-runtime";
2458
2623
  function themeProps(theme) {
2459
2624
  const safeTheme = resolveThemeAppearance(normalizeFormTheme(theme));
2460
2625
  if (!safeTheme) return {};
@@ -2476,10 +2641,10 @@ function badgeUrl(href) {
2476
2641
  url.searchParams.set("utm_source", "form_badge");
2477
2642
  return url.toString();
2478
2643
  }
2479
- var FOCUSABLE_SELECTOR = 'input:not([disabled]), textarea:not([disabled]), select:not([disabled]), button:not([disabled]), [tabindex]:not([tabindex="-1"])';
2644
+ var FOCUSABLE_SELECTOR2 = 'input:not([disabled]), textarea:not([disabled]), select:not([disabled]), button:not([disabled]), [tabindex]:not([tabindex="-1"])';
2480
2645
  function resolveFocusable(el) {
2481
2646
  if (!el) return null;
2482
- return el.matches(FOCUSABLE_SELECTOR) ? el : el.querySelector(FOCUSABLE_SELECTOR);
2647
+ return el.matches(FOCUSABLE_SELECTOR2) ? el : el.querySelector(FOCUSABLE_SELECTOR2);
2483
2648
  }
2484
2649
  function nextFrame(run) {
2485
2650
  if (typeof requestAnimationFrame === "function") requestAnimationFrame(run);
@@ -2511,7 +2676,7 @@ function assertHpHidden(el) {
2511
2676
  function FilloForm(props) {
2512
2677
  const { form, formId } = props;
2513
2678
  const renderOnly = props.renderOnly === true || props.skipValidation === true;
2514
- const client = useMemo5(
2679
+ const client = useMemo6(
2515
2680
  () => renderOnly ? void 0 : props.client ?? (formId ? createClient() : void 0),
2516
2681
  [renderOnly, props.client, formId]
2517
2682
  );
@@ -2548,11 +2713,11 @@ function FilloForm(props) {
2548
2713
  });
2549
2714
  const renderResolvedSnapshot = Boolean(resolvedSchema && !devChrome);
2550
2715
  const inlineSchema = codeForm ? renderResolvedSnapshot ? resolvedSchema : codeForm.schema : form;
2551
- const onErrorRef = useRef8(onError);
2552
- useEffect8(() => {
2716
+ const onErrorRef = useRef9(onError);
2717
+ useEffect9(() => {
2553
2718
  onErrorRef.current = onError;
2554
2719
  });
2555
- const targetError = useMemo5(
2720
+ const targetError = useMemo6(
2556
2721
  () => form && !formId && !renderOnly && (!isCodeForm2(form) || !props.client) ? new FilloError2(
2557
2722
  "This form has no Fillo target. Pass formId with the schema, use a defineForm() value with client, or set renderOnly for a non-submitting preview.",
2558
2723
  422,
@@ -2561,11 +2726,11 @@ function FilloForm(props) {
2561
2726
  ) : null,
2562
2727
  [form, formId, props.client, renderOnly]
2563
2728
  );
2564
- useEffect8(() => {
2729
+ useEffect9(() => {
2565
2730
  if (targetError) onErrorRef.current?.(targetError);
2566
2731
  }, [targetError]);
2567
2732
  const needsFetch = !inlineSchema && Boolean(client && formId);
2568
- useEffect8(() => {
2733
+ useEffect9(() => {
2569
2734
  if (!needsFetch || !client || !formId) {
2570
2735
  setLoaded(null);
2571
2736
  setLoadFailure(null);
@@ -2602,7 +2767,7 @@ function FilloForm(props) {
2602
2767
  const currentLoaded = needsFetch && loaded?.formId === formId ? loaded : null;
2603
2768
  const loadError = needsFetch && loadFailure && loadFailure.formId === formId ? loadFailure.error : null;
2604
2769
  const rawSchema = inlineSchema ?? currentLoaded?.schema ?? null;
2605
- const normalized = useMemo5(
2770
+ const normalized = useMemo6(
2606
2771
  () => rawSchema ? normalizeFormSchema(rawSchema) : null,
2607
2772
  [rawSchema]
2608
2773
  );
@@ -2610,8 +2775,8 @@ function FilloForm(props) {
2610
2775
  const resolvedCodeTheme = renderResolvedSnapshot ? resolvedTheme ?? void 0 : codeForm?.theme;
2611
2776
  const theme = props.appearance?.theme ?? props.theme ?? resolvedCodeTheme ?? currentLoaded?.theme ?? void 0;
2612
2777
  if (targetError) {
2613
- if (props.renderError) return /* @__PURE__ */ jsx7(Fragment3, { children: props.renderError(targetError) });
2614
- return /* @__PURE__ */ jsx7(
2778
+ if (props.renderError) return /* @__PURE__ */ jsx8(Fragment3, { children: props.renderError(targetError) });
2779
+ return /* @__PURE__ */ jsx8(
2615
2780
  "div",
2616
2781
  {
2617
2782
  className: `fillo-form fillo-form--error ${props.className ?? ""}`,
@@ -2626,15 +2791,15 @@ function FilloForm(props) {
2626
2791
  }
2627
2792
  if (normalized && !normalized.ok) {
2628
2793
  const error = new FilloError2(`This form could not be rendered: ${normalized.error}`, 422);
2629
- if (props.renderError) return /* @__PURE__ */ jsx7(Fragment3, { children: props.renderError(error) });
2630
- return /* @__PURE__ */ jsx7("div", { className: `fillo-form fillo-form--error ${props.className ?? ""}`, children: strings.renderFailed });
2794
+ if (props.renderError) return /* @__PURE__ */ jsx8(Fragment3, { children: props.renderError(error) });
2795
+ return /* @__PURE__ */ jsx8("div", { className: `fillo-form fillo-form--error ${props.className ?? ""}`, children: strings.renderFailed });
2631
2796
  }
2632
2797
  if (loadError) {
2633
2798
  if (props.renderError) {
2634
- return /* @__PURE__ */ jsx7(Fragment3, { children: props.renderError(loadError) });
2799
+ return /* @__PURE__ */ jsx8(Fragment3, { children: props.renderError(loadError) });
2635
2800
  }
2636
2801
  const message = loadError.status === 404 ? strings.loadFailedNotFound : loadError.status === 0 ? strings.loadFailedNetwork : strings.loadFailed;
2637
- return /* @__PURE__ */ jsx7("div", { className: `fillo-form fillo-form--error ${props.className ?? ""}`, children: message });
2802
+ return /* @__PURE__ */ jsx8("div", { className: `fillo-form fillo-form--error ${props.className ?? ""}`, children: message });
2638
2803
  }
2639
2804
  if (currentLoaded?.accepting === false && schema) {
2640
2805
  const variant = overlayVariant(currentLoaded.acceptingReason);
@@ -2650,9 +2815,9 @@ function FilloForm(props) {
2650
2815
  void 0,
2651
2816
  currentLoaded.acceptingReason
2652
2817
  );
2653
- return /* @__PURE__ */ jsx7(Fragment3, { children: props.renderError(error) });
2818
+ return /* @__PURE__ */ jsx8(Fragment3, { children: props.renderError(error) });
2654
2819
  }
2655
- return /* @__PURE__ */ jsx7(
2820
+ return /* @__PURE__ */ jsx8(
2656
2821
  NotOpenOverlay,
2657
2822
  {
2658
2823
  ...props,
@@ -2664,21 +2829,21 @@ function FilloForm(props) {
2664
2829
  );
2665
2830
  }
2666
2831
  if (currentLoaded?.closed) {
2667
- return /* @__PURE__ */ jsx7(
2832
+ return /* @__PURE__ */ jsx8(
2668
2833
  "div",
2669
2834
  {
2670
2835
  className: `fillo-form fillo-form--closed ${props.className ?? ""}`,
2671
2836
  ...themeProps(theme),
2672
- children: /* @__PURE__ */ jsx7("p", { className: "fillo-closed", children: strings.closed })
2837
+ children: /* @__PURE__ */ jsx8("p", { className: "fillo-closed", children: strings.closed })
2673
2838
  }
2674
2839
  );
2675
2840
  }
2676
2841
  const canonicalSyncError = codeForm && syncError && !props.skipValidation ? syncError : null;
2677
2842
  if (canonicalSyncError && props.renderError) {
2678
- return /* @__PURE__ */ jsx7(Fragment3, { children: props.renderError(canonicalSyncError) });
2843
+ return /* @__PURE__ */ jsx8(Fragment3, { children: props.renderError(canonicalSyncError) });
2679
2844
  }
2680
2845
  if (canonicalSyncError && !devChrome) {
2681
- return /* @__PURE__ */ jsx7(
2846
+ return /* @__PURE__ */ jsx8(
2682
2847
  "div",
2683
2848
  {
2684
2849
  className: `fillo-form fillo-form--error ${props.className ?? ""}`,
@@ -2700,9 +2865,9 @@ function FilloForm(props) {
2700
2865
  void 0,
2701
2866
  syncedAcceptingReason
2702
2867
  ) : new FilloError2("This form isn't published yet.", 403);
2703
- if (props.renderError) return /* @__PURE__ */ jsx7(Fragment3, { children: props.renderError(error) });
2868
+ if (props.renderError) return /* @__PURE__ */ jsx8(Fragment3, { children: props.renderError(error) });
2704
2869
  if (schema) {
2705
- return /* @__PURE__ */ jsx7(
2870
+ return /* @__PURE__ */ jsx8(
2706
2871
  NotOpenOverlay,
2707
2872
  {
2708
2873
  ...props,
@@ -2713,27 +2878,27 @@ function FilloForm(props) {
2713
2878
  }
2714
2879
  );
2715
2880
  }
2716
- return /* @__PURE__ */ jsx7(
2881
+ return /* @__PURE__ */ jsx8(
2717
2882
  "div",
2718
2883
  {
2719
2884
  className: `fillo-form fillo-form--closed ${props.className ?? ""}`,
2720
2885
  ...themeProps(theme),
2721
- children: /* @__PURE__ */ jsx7("p", { className: "fillo-closed", children: variant === "closed" ? strings.closed : strings.notLive })
2886
+ children: /* @__PURE__ */ jsx8("p", { className: "fillo-closed", children: variant === "closed" ? strings.closed : strings.notLive })
2722
2887
  }
2723
2888
  );
2724
2889
  }
2725
2890
  const awaitingCanonical = Boolean(codeForm && codeFormSyncing && !props.skipValidation);
2726
2891
  if (!devChrome && awaitingCanonical || !schema) {
2727
- return /* @__PURE__ */ jsxs6(
2892
+ return /* @__PURE__ */ jsxs7(
2728
2893
  "div",
2729
2894
  {
2730
2895
  className: `fillo-form fillo-form--loading ${props.className ?? ""}`,
2731
2896
  "aria-busy": "true",
2732
2897
  ...themeProps(theme),
2733
2898
  children: [
2734
- /* @__PURE__ */ jsx7("div", { className: "fillo-skeleton" }),
2735
- /* @__PURE__ */ jsx7("div", { className: "fillo-skeleton" }),
2736
- /* @__PURE__ */ jsx7("div", { className: "fillo-skeleton fillo-skeleton--short" })
2899
+ /* @__PURE__ */ jsx8("div", { className: "fillo-skeleton" }),
2900
+ /* @__PURE__ */ jsx8("div", { className: "fillo-skeleton" }),
2901
+ /* @__PURE__ */ jsx8("div", { className: "fillo-skeleton fillo-skeleton--short" })
2737
2902
  ]
2738
2903
  }
2739
2904
  );
@@ -2741,7 +2906,7 @@ function FilloForm(props) {
2741
2906
  const branding = codeForm ? syncedBranding : currentLoaded?.branding;
2742
2907
  const poweredBy = branding?.poweredBy ?? true;
2743
2908
  const challenge = props.challenge ?? (codeForm ? syncedChallenge : currentLoaded?.challenge) ?? void 0;
2744
- return /* @__PURE__ */ jsx7(
2909
+ return /* @__PURE__ */ jsx8(
2745
2910
  ResolvedForm,
2746
2911
  {
2747
2912
  ...props,
@@ -2766,7 +2931,7 @@ function FilloForm(props) {
2766
2931
  );
2767
2932
  }
2768
2933
  function PoweredBy({ label, href }) {
2769
- return /* @__PURE__ */ jsx7("a", { className: "fillo-powered", href: badgeUrl(href), target: "_blank", rel: "noopener noreferrer", children: label ?? "Powered by Fillo" });
2934
+ return /* @__PURE__ */ jsx8("a", { className: "fillo-powered", href: badgeUrl(href), target: "_blank", rel: "noopener noreferrer", children: label ?? "Powered by Fillo" });
2770
2935
  }
2771
2936
  function overlayVariant(reason) {
2772
2937
  return reason === "expired" || reason === "capped" || reason === "storage_full" ? "closed" : "notOpen";
@@ -2774,7 +2939,7 @@ function overlayVariant(reason) {
2774
2939
  function NotOpenOverlay(props) {
2775
2940
  const strings = resolveStrings(props.strings);
2776
2941
  const closedFlavor = props.variant === "closed";
2777
- return /* @__PURE__ */ jsxs6(
2942
+ return /* @__PURE__ */ jsxs7(
2778
2943
  "div",
2779
2944
  {
2780
2945
  className: `fillo-form fillo-form--not-open ${props.className ?? ""}`,
@@ -2782,13 +2947,13 @@ function NotOpenOverlay(props) {
2782
2947
  "data-state": "closed",
2783
2948
  ...themeProps(props.theme),
2784
2949
  children: [
2785
- /* @__PURE__ */ jsx7(
2950
+ /* @__PURE__ */ jsx8(
2786
2951
  "div",
2787
2952
  {
2788
2953
  className: "fillo-not-open-preview",
2789
2954
  "aria-hidden": "true",
2790
2955
  ref: (node) => node?.setAttribute("inert", ""),
2791
- children: /* @__PURE__ */ jsx7(
2956
+ children: /* @__PURE__ */ jsx8(
2792
2957
  ResolvedForm,
2793
2958
  {
2794
2959
  schema: props.schema,
@@ -2804,11 +2969,11 @@ function NotOpenOverlay(props) {
2804
2969
  )
2805
2970
  }
2806
2971
  ),
2807
- /* @__PURE__ */ jsxs6("div", { className: "fillo-not-open-card", "data-fillo": "not-open-card", role: "status", children: [
2808
- /* @__PURE__ */ jsx7("h2", { className: "fillo-not-open-title", children: closedFlavor ? strings.closedTitle : strings.notOpenTitle }),
2809
- /* @__PURE__ */ jsx7("p", { className: "fillo-not-open-body", children: closedFlavor ? strings.closed : strings.notOpenBody })
2972
+ /* @__PURE__ */ jsxs7("div", { className: "fillo-not-open-card", "data-fillo": "not-open-card", role: "status", children: [
2973
+ /* @__PURE__ */ jsx8("h2", { className: "fillo-not-open-title", children: closedFlavor ? strings.closedTitle : strings.notOpenTitle }),
2974
+ /* @__PURE__ */ jsx8("p", { className: "fillo-not-open-body", children: closedFlavor ? strings.closed : strings.notOpenBody })
2810
2975
  ] }),
2811
- (props.branding?.poweredBy ?? true) && /* @__PURE__ */ jsx7(PoweredBy, { label: props.branding?.label, href: props.branding?.href })
2976
+ (props.branding?.poweredBy ?? true) && /* @__PURE__ */ jsx8(PoweredBy, { label: props.branding?.label, href: props.branding?.href })
2812
2977
  ]
2813
2978
  }
2814
2979
  );
@@ -2825,26 +2990,26 @@ function ResolvedForm(props) {
2825
2990
  const instanceId = useId2().replace(/:/g, "");
2826
2991
  const devEnv = useIsDevEnv();
2827
2992
  const devChrome = !displayOnly && (props.preview === true || devEnv);
2828
- const hpRef = useRef8(null);
2993
+ const hpRef = useRef9(null);
2829
2994
  const codeForm = isCodeForm2(props.form) ? props.form : null;
2830
2995
  const client = props.client;
2831
2996
  const challenge = renderOnly ? void 0 : props.challenge;
2832
2997
  const challengeRequired = Boolean(challenge);
2833
- const challengeTokenRef = useRef8(void 0);
2998
+ const challengeTokenRef = useRef9(void 0);
2834
2999
  const [resyncWarning, setResyncWarning] = useState6(null);
2835
3000
  const [resolutionFailureCode, setResolutionFailureCode] = useState6(null);
2836
3001
  const [challengeToken, setChallengeToken] = useState6(void 0);
2837
3002
  const [challengeError, setChallengeError] = useState6(false);
2838
- const resetChallengeRef = useRef8(void 0);
2839
- const handleChallengeToken = useCallback((token) => {
3003
+ const resetChallengeRef = useRef9(void 0);
3004
+ const handleChallengeToken = useCallback2((token) => {
2840
3005
  challengeTokenRef.current = token;
2841
3006
  setChallengeToken(token);
2842
3007
  if (token) setChallengeError(false);
2843
3008
  }, []);
2844
- const registerChallengeReset = useCallback((reset) => {
3009
+ const registerChallengeReset = useCallback2((reset) => {
2845
3010
  resetChallengeRef.current = reset;
2846
3011
  }, []);
2847
- const resolveFormId = useMemo5(
3012
+ const resolveFormId = useMemo6(
2848
3013
  () => codeForm && client?.key ? (
2849
3014
  // bypassCache: a stale cached formId must not be trusted at submit time.
2850
3015
  async () => {
@@ -2942,8 +3107,8 @@ function ResolvedForm(props) {
2942
3107
  });
2943
3108
  useDraftFlush(api, !displayOnly && !renderOnly);
2944
3109
  const strings = resolveStrings(props.strings);
2945
- const announceRef = useRef8(null);
2946
- const announce = useCallback((text) => {
3110
+ const announceRef = useRef9(null);
3111
+ const announce = useCallback2((text) => {
2947
3112
  const el = announceRef.current;
2948
3113
  if (!el) return;
2949
3114
  if (el.textContent === text) {
@@ -2955,20 +3120,20 @@ function ResolvedForm(props) {
2955
3120
  }
2956
3121
  el.textContent = text;
2957
3122
  }, []);
2958
- useEffect8(() => {
3123
+ useEffect9(() => {
2959
3124
  if (api.status === "submitting") announce(strings.submittingAnnouncement);
2960
3125
  }, [api.status, announce, strings.submittingAnnouncement]);
2961
3126
  const resuming = api.resumedDraft || api.editingPrevious;
2962
3127
  const resumeAnnouncement = api.editingPrevious ? strings.editNotice : strings.resumeNotice;
2963
- useEffect8(() => {
3128
+ useEffect9(() => {
2964
3129
  if (resuming) announce(resumeAnnouncement);
2965
3130
  }, [resuming, resumeAnnouncement, announce]);
2966
- const themed = useMemo5(() => themeProps(theme), [theme]);
3131
+ const themed = useMemo6(() => themeProps(theme), [theme]);
2967
3132
  const warningCode = resyncWarning ? resyncWarning.code : props.syncWarningCode;
2968
3133
  const warningUrl = resyncWarning ? resyncWarning.url : props.syncWarningUrl;
2969
3134
  const uploadsAvailable = resyncWarning ? resyncWarning.uploadsAvailable : props.uploadsAvailable;
2970
3135
  const uploadFileSizeLimitMb = resyncWarning ? resyncWarning.uploadFileSizeLimitMb : props.uploadFileSizeLimitMb;
2971
- const chromeValue = useMemo5(
3136
+ const chromeValue = useMemo6(
2972
3137
  () => ({
2973
3138
  devChrome,
2974
3139
  renderOnly,
@@ -2982,18 +3147,18 @@ function ResolvedForm(props) {
2982
3147
  const settings = schema.settings;
2983
3148
  const submitted = api.status === "submitted";
2984
3149
  const liveSubmit = submitted && !api.restoredSubmission;
2985
- useEffect8(() => {
3150
+ useEffect9(() => {
2986
3151
  if (!liveSubmit || !settings.redirectUrl || typeof window === "undefined") return;
2987
3152
  const url = safeHttpUrl(settings.redirectUrl);
2988
3153
  if (url) window.location.assign(url);
2989
3154
  }, [liveSubmit, settings.redirectUrl]);
2990
- const successRef = useRef8(null);
2991
- useEffect8(() => {
3155
+ const successRef = useRef9(null);
3156
+ useEffect9(() => {
2992
3157
  if (liveSubmit) successRef.current?.focus();
2993
3158
  }, [liveSubmit]);
2994
- const blocksRef = useRef8(null);
2995
- const prevPageIndex = useRef8(api.pageIndex);
2996
- useEffect8(() => {
3159
+ const blocksRef = useRef9(null);
3160
+ const prevPageIndex = useRef9(api.pageIndex);
3161
+ useEffect9(() => {
2997
3162
  if (prevPageIndex.current === api.pageIndex) return;
2998
3163
  prevPageIndex.current = api.pageIndex;
2999
3164
  const container = blocksRef.current?.parentElement;
@@ -3005,7 +3170,7 @@ function ResolvedForm(props) {
3005
3170
  }, [api.pageIndex]);
3006
3171
  const appearance = props.appearance;
3007
3172
  if (api.status === "submitted") {
3008
- return /* @__PURE__ */ jsxs6(
3173
+ return /* @__PURE__ */ jsxs7(
3009
3174
  "div",
3010
3175
  {
3011
3176
  ref: successRef,
@@ -3022,7 +3187,7 @@ function ResolvedForm(props) {
3022
3187
  "data-state": "submitted",
3023
3188
  ...themed,
3024
3189
  children: [
3025
- props.renderSuccess ? props.renderSuccess() : /* @__PURE__ */ jsxs6(
3190
+ props.renderSuccess ? props.renderSuccess() : /* @__PURE__ */ jsxs7(
3026
3191
  "div",
3027
3192
  {
3028
3193
  className: slotClass3("fillo-success", appearance, {
@@ -3031,13 +3196,13 @@ function ResolvedForm(props) {
3031
3196
  }),
3032
3197
  "data-fillo": "success",
3033
3198
  children: [
3034
- /* @__PURE__ */ jsx7("div", { className: "fillo-success-mark", "aria-hidden": "true" }),
3035
- /* @__PURE__ */ jsx7("h2", { className: "fillo-success-title", children: settings.successTitle ?? strings.successTitle }),
3036
- /* @__PURE__ */ jsx7("p", { className: "fillo-success-message", children: api.duplicateSubmission ? strings.alreadyAnswered : settings.successMessage ?? strings.successMessage })
3199
+ /* @__PURE__ */ jsx8("div", { className: "fillo-success-mark", "aria-hidden": "true" }),
3200
+ /* @__PURE__ */ jsx8("h2", { className: "fillo-success-title", children: settings.successTitle ?? strings.successTitle }),
3201
+ /* @__PURE__ */ jsx8("p", { className: "fillo-success-message", children: api.duplicateSubmission ? strings.alreadyAnswered : settings.successMessage ?? strings.successMessage })
3037
3202
  ]
3038
3203
  }
3039
3204
  ),
3040
- props.poweredBy && /* @__PURE__ */ jsx7(PoweredBy, { label: props.brandLabel, href: props.brandHref })
3205
+ props.poweredBy && /* @__PURE__ */ jsx8(PoweredBy, { label: props.brandLabel, href: props.brandHref })
3041
3206
  ]
3042
3207
  }
3043
3208
  );
@@ -3058,8 +3223,8 @@ function ResolvedForm(props) {
3058
3223
  slot: "root",
3059
3224
  status: api.status
3060
3225
  });
3061
- const formBody = /* @__PURE__ */ jsxs6(Fragment3, { children: [
3062
- /* @__PURE__ */ jsx7(
3226
+ const formBody = /* @__PURE__ */ jsxs7(Fragment3, { children: [
3227
+ /* @__PURE__ */ jsx8(
3063
3228
  "span",
3064
3229
  {
3065
3230
  ref: announceRef,
@@ -3069,7 +3234,7 @@ function ResolvedForm(props) {
3069
3234
  "data-fillo": "announce"
3070
3235
  }
3071
3236
  ),
3072
- devChrome && /* @__PURE__ */ jsx7(
3237
+ devChrome && /* @__PURE__ */ jsx8(
3073
3238
  DevChrome,
3074
3239
  {
3075
3240
  preview: props.preview,
@@ -3081,18 +3246,18 @@ function ResolvedForm(props) {
3081
3246
  noClient: !props.client && !props.skipValidation && !renderOnly
3082
3247
  }
3083
3248
  ),
3084
- resuming && /* @__PURE__ */ jsxs6(
3249
+ resuming && /* @__PURE__ */ jsxs7(
3085
3250
  "div",
3086
3251
  {
3087
3252
  className: slotClass3("fillo-resume", appearance, { slot: "resume" }),
3088
3253
  "data-fillo": "resume",
3089
3254
  children: [
3090
- /* @__PURE__ */ jsx7("span", { className: "fillo-resume-text", children: api.editingPrevious ? strings.editNotice : strings.resumeNotice }),
3091
- /* @__PURE__ */ jsx7("button", { type: "button", className: "fillo-resume-clear", onClick: api.resetDraft, children: strings.resumeStartOver })
3255
+ /* @__PURE__ */ jsx8("span", { className: "fillo-resume-text", children: api.editingPrevious ? strings.editNotice : strings.resumeNotice }),
3256
+ /* @__PURE__ */ jsx8("button", { type: "button", className: "fillo-resume-clear", onClick: api.resetDraft, children: strings.resumeStartOver })
3092
3257
  ]
3093
3258
  }
3094
3259
  ),
3095
- api.resumeLinkFailed && !api.resumedDraft && !api.editingPrevious && /* @__PURE__ */ jsx7(
3260
+ api.resumeLinkFailed && !api.resumedDraft && !api.editingPrevious && /* @__PURE__ */ jsx8(
3096
3261
  "div",
3097
3262
  {
3098
3263
  className: slotClass3("fillo-resume fillo-resume--expired", appearance, {
@@ -3100,10 +3265,10 @@ function ResolvedForm(props) {
3100
3265
  }),
3101
3266
  "data-fillo": "resume",
3102
3267
  role: "status",
3103
- children: /* @__PURE__ */ jsx7("span", { className: "fillo-resume-text", children: strings.resumeLinkExpired })
3268
+ children: /* @__PURE__ */ jsx8("span", { className: "fillo-resume-text", children: strings.resumeLinkExpired })
3104
3269
  }
3105
3270
  ),
3106
- multiPage && settings.showProgress !== false && /* @__PURE__ */ jsx7(
3271
+ multiPage && settings.showProgress !== false && /* @__PURE__ */ jsx8(
3107
3272
  "div",
3108
3273
  {
3109
3274
  className: slotClass3("fillo-progress-track", appearance, { slot: "progress" }),
@@ -3113,7 +3278,7 @@ function ResolvedForm(props) {
3113
3278
  "aria-valuemin": 1,
3114
3279
  "aria-valuemax": progressTotal,
3115
3280
  "aria-valuenow": progressStep,
3116
- children: /* @__PURE__ */ jsx7(
3281
+ children: /* @__PURE__ */ jsx8(
3117
3282
  "div",
3118
3283
  {
3119
3284
  className: slotClass3("fillo-progress-fill", appearance, { slot: "progressFill" }),
@@ -3125,13 +3290,13 @@ function ResolvedForm(props) {
3125
3290
  )
3126
3291
  }
3127
3292
  ),
3128
- api.pageIndex === 0 && props.showTitle !== false && /* @__PURE__ */ jsxs6(
3293
+ api.pageIndex === 0 && props.showTitle !== false && /* @__PURE__ */ jsxs7(
3129
3294
  "header",
3130
3295
  {
3131
3296
  className: slotClass3("fillo-header", appearance, { slot: "header" }),
3132
3297
  "data-fillo": "header",
3133
3298
  children: [
3134
- schema.title && /* @__PURE__ */ jsx7(
3299
+ schema.title && /* @__PURE__ */ jsx8(
3135
3300
  "h1",
3136
3301
  {
3137
3302
  className: slotClass3("fillo-title", appearance, { slot: "title" }),
@@ -3139,7 +3304,7 @@ function ResolvedForm(props) {
3139
3304
  children: schema.title
3140
3305
  }
3141
3306
  ),
3142
- schema.description && /* @__PURE__ */ jsx7(
3307
+ schema.description && /* @__PURE__ */ jsx8(
3143
3308
  "p",
3144
3309
  {
3145
3310
  className: slotClass3("fillo-form-description", appearance, { slot: "description" }),
@@ -3150,7 +3315,7 @@ function ResolvedForm(props) {
3150
3315
  ]
3151
3316
  }
3152
3317
  ),
3153
- multiPage && api.page.title && api.pageIndex > 0 && /* @__PURE__ */ jsx7(
3318
+ multiPage && api.page.title && api.pageIndex > 0 && /* @__PURE__ */ jsx8(
3154
3319
  "h2",
3155
3320
  {
3156
3321
  className: slotClass3("fillo-page-title", appearance, { slot: "pageTitle" }),
@@ -3158,14 +3323,14 @@ function ResolvedForm(props) {
3158
3323
  children: api.page.title
3159
3324
  }
3160
3325
  ),
3161
- /* @__PURE__ */ jsx7(
3326
+ /* @__PURE__ */ jsx8(
3162
3327
  "div",
3163
3328
  {
3164
3329
  className: slotClass3("fillo-blocks", appearance, { slot: "blocks" }),
3165
3330
  "data-fillo": "blocks",
3166
3331
  ref: blocksRef,
3167
3332
  tabIndex: -1,
3168
- children: api.blocks.map((block) => /* @__PURE__ */ jsx7(
3333
+ children: api.blocks.map((block) => /* @__PURE__ */ jsx8(
3169
3334
  BlockRenderer,
3170
3335
  {
3171
3336
  block,
@@ -3177,7 +3342,7 @@ function ResolvedForm(props) {
3177
3342
  ))
3178
3343
  }
3179
3344
  ),
3180
- !displayOnly && /* @__PURE__ */ jsx7(
3345
+ !displayOnly && /* @__PURE__ */ jsx8(
3181
3346
  "input",
3182
3347
  {
3183
3348
  type: "text",
@@ -3194,8 +3359,8 @@ function ResolvedForm(props) {
3194
3359
  }
3195
3360
  }
3196
3361
  ),
3197
- challenge && showChallenge && /* @__PURE__ */ jsxs6("div", { className: "fillo-turnstile-slot", "data-fillo": "turnstile-slot", children: [
3198
- /* @__PURE__ */ jsx7(
3362
+ challenge && showChallenge && /* @__PURE__ */ jsxs7("div", { className: "fillo-turnstile-slot", "data-fillo": "turnstile-slot", children: [
3363
+ /* @__PURE__ */ jsx8(
3199
3364
  TurnstileWidget,
3200
3365
  {
3201
3366
  siteKey: challenge.siteKey,
@@ -3205,24 +3370,24 @@ function ResolvedForm(props) {
3205
3370
  registerReset: registerChallengeReset
3206
3371
  }
3207
3372
  ),
3208
- challengeError && /* @__PURE__ */ jsx7("p", { className: "fillo-turnstile-error", role: "alert", children: strings.challengeUnavailable })
3373
+ challengeError && /* @__PURE__ */ jsx8("p", { className: "fillo-turnstile-error", role: "alert", children: strings.challengeUnavailable })
3209
3374
  ] }),
3210
- (api.status === "error" || api.submitError) && /* @__PURE__ */ jsxs6("p", { className: "fillo-submit-error", role: "alert", children: [
3375
+ (api.status === "error" || api.submitError) && /* @__PURE__ */ jsxs7("p", { className: "fillo-submit-error", role: "alert", children: [
3211
3376
  api.submitError ?? strings.submitFailed,
3212
- submitFixUrl && /* @__PURE__ */ jsxs6(Fragment3, { children: [
3377
+ submitFixUrl && /* @__PURE__ */ jsxs7(Fragment3, { children: [
3213
3378
  " ",
3214
3379
  "Connect storage:",
3215
3380
  " ",
3216
- /* @__PURE__ */ jsx7("a", { href: submitFixUrl, target: "_blank", rel: "noopener noreferrer", children: submitFixUrl })
3381
+ /* @__PURE__ */ jsx8("a", { href: submitFixUrl, target: "_blank", rel: "noopener noreferrer", children: submitFixUrl })
3217
3382
  ] })
3218
3383
  ] }),
3219
- showFooter && /* @__PURE__ */ jsxs6(
3384
+ showFooter && /* @__PURE__ */ jsxs7(
3220
3385
  "footer",
3221
3386
  {
3222
3387
  className: slotClass3("fillo-footer", appearance, { slot: "footer" }),
3223
3388
  "data-fillo": "footer",
3224
3389
  children: [
3225
- multiPage && !api.isFirstPage && /* @__PURE__ */ jsx7(
3390
+ multiPage && !api.isFirstPage && /* @__PURE__ */ jsx8(
3226
3391
  "button",
3227
3392
  {
3228
3393
  type: "button",
@@ -3235,7 +3400,7 @@ function ResolvedForm(props) {
3235
3400
  children: strings.back
3236
3401
  }
3237
3402
  ),
3238
- showPrimaryButton && /* @__PURE__ */ jsx7(
3403
+ showPrimaryButton && /* @__PURE__ */ jsx8(
3239
3404
  "button",
3240
3405
  {
3241
3406
  type: "submit",
@@ -3254,23 +3419,23 @@ function ResolvedForm(props) {
3254
3419
  ]
3255
3420
  }
3256
3421
  ),
3257
- props.poweredBy && /* @__PURE__ */ jsx7(PoweredBy, { label: props.brandLabel, href: props.brandHref })
3422
+ props.poweredBy && /* @__PURE__ */ jsx8(PoweredBy, { label: props.brandLabel, href: props.brandHref })
3258
3423
  ] });
3259
- 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 ? (
3424
+ return /* @__PURE__ */ jsx8(FilloContext.Provider, { value: api, children: /* @__PURE__ */ jsx8(FilloChromeContext.Provider, { value: chromeValue, children: /* @__PURE__ */ jsx8(FilloAnnounceContext.Provider, { value: announce, children: /* @__PURE__ */ jsx8(FilloStringsContext.Provider, { value: strings, children: /* @__PURE__ */ jsx8(FilloAppearanceContext.Provider, { value: appearance, children: /* @__PURE__ */ jsx8(FilloInstanceIdContext.Provider, { value: instanceId, children: displayOnly ? (
3260
3425
  // Display-only preview: a plain div (no form element to submit) with
3261
3426
  // every control inside a natively disabled fieldset — unfocusable
3262
3427
  // and unfillable even without the stylesheet or inert support.
3263
- /* @__PURE__ */ jsx7(
3428
+ /* @__PURE__ */ jsx8(
3264
3429
  "div",
3265
3430
  {
3266
3431
  className: rootClass,
3267
3432
  "data-fillo": "preview-form",
3268
3433
  "data-state": "preview",
3269
3434
  ...themed,
3270
- children: /* @__PURE__ */ jsx7("fieldset", { disabled: true, className: "fillo-not-open-fields", children: formBody })
3435
+ children: /* @__PURE__ */ jsx8("fieldset", { disabled: true, className: "fillo-not-open-fields", children: formBody })
3271
3436
  }
3272
3437
  )
3273
- ) : /* @__PURE__ */ jsx7(
3438
+ ) : /* @__PURE__ */ jsx8(
3274
3439
  "form",
3275
3440
  {
3276
3441
  className: rootClass,
@@ -3298,21 +3463,21 @@ function ResolvedForm(props) {
3298
3463
  }
3299
3464
 
3300
3465
  // src/jsx.tsx
3301
- import { useRef as useRef9 } from "react";
3466
+ import { useRef as useRef10 } from "react";
3302
3467
  import {
3303
3468
  codeFormFromJsx,
3304
3469
  FilloJsxError,
3305
3470
  isLikelyDevEnv as isLikelyDevEnv2,
3306
3471
  JSX_BLOCK_COMPONENTS
3307
3472
  } from "@usefillo/core";
3308
- import { jsx as jsx8 } from "react/jsx-runtime";
3473
+ import { jsx as jsx9 } from "react/jsx-runtime";
3309
3474
  var B = JSX_BLOCK_COMPONENTS;
3310
3475
  var isDevEnv = () => isLikelyDevEnv2();
3311
3476
  function JsxForm(props) {
3312
3477
  const { id, title, description, settings, theme, client, children, ...rest } = props;
3313
3478
  const compiled = codeFormFromJsx({ id, title, description, settings, theme }, children);
3314
3479
  const json = JSON.stringify(compiled.schema) + JSON.stringify(compiled.theme ?? null);
3315
- const memo2 = useRef9(null);
3480
+ const memo2 = useRef10(null);
3316
3481
  if (memo2.current && memo2.current.json !== json && isDevEnv()) {
3317
3482
  console.warn(
3318
3483
  `[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.`
@@ -3321,7 +3486,7 @@ function JsxForm(props) {
3321
3486
  if (!memo2.current || memo2.current.json !== json) {
3322
3487
  memo2.current = { json, form: compiled };
3323
3488
  }
3324
- return /* @__PURE__ */ jsx8(FilloForm, { ...{ form: memo2.current.form, client, ...rest } });
3489
+ return /* @__PURE__ */ jsx9(FilloForm, { ...{ form: memo2.current.form, client, ...rest } });
3325
3490
  }
3326
3491
  function defineFormFromJsx(element) {
3327
3492
  if (!element || element.type !== JsxForm) {
@@ -3366,7 +3531,7 @@ var Fillo = {
3366
3531
  };
3367
3532
 
3368
3533
  // src/provider.tsx
3369
- import { useEffect as useEffect9, useId as useId3, useMemo as useMemo6, useState as useState7 } from "react";
3534
+ import { useEffect as useEffect10, useId as useId3, useMemo as useMemo7, useState as useState7 } from "react";
3370
3535
  import {
3371
3536
  FilloError as FilloError3,
3372
3537
  createClient as createClient2,
@@ -3376,7 +3541,7 @@ import {
3376
3541
  resolveStrings as resolveStrings2,
3377
3542
  syncCodeForm as syncCodeForm3
3378
3543
  } from "@usefillo/core";
3379
- import { Fragment as Fragment4, jsx as jsx9 } from "react/jsx-runtime";
3544
+ import { Fragment as Fragment4, jsx as jsx10 } from "react/jsx-runtime";
3380
3545
  function FilloProvider({
3381
3546
  children,
3382
3547
  form,
@@ -3392,7 +3557,7 @@ function FilloProvider({
3392
3557
  ...options
3393
3558
  }) {
3394
3559
  const localPreview = renderOnly === true || skipValidation === true;
3395
- const activeClient = useMemo6(
3560
+ const activeClient = useMemo7(
3396
3561
  () => localPreview ? void 0 : client ?? (formId ? createClient2() : void 0),
3397
3562
  [client, formId, localPreview]
3398
3563
  );
@@ -3402,7 +3567,7 @@ function FilloProvider({
3402
3567
  const devChrome = preview === true || devEnv;
3403
3568
  if (preview === true) warnPreviewInProduction();
3404
3569
  const codeForm = isCodeForm2(form) ? form : null;
3405
- const targetError = useMemo6(
3570
+ const targetError = useMemo7(
3406
3571
  () => !formId && !localPreview && (!codeForm || !client) ? new FilloError3(
3407
3572
  "This form has no Fillo target. Pass formId with the schema, use a defineForm() value with client, or set renderOnly for a non-submitting preview.",
3408
3573
  422,
@@ -3411,7 +3576,7 @@ function FilloProvider({
3411
3576
  ) : null,
3412
3577
  [client, codeForm, formId, localPreview]
3413
3578
  );
3414
- useEffect9(() => {
3579
+ useEffect10(() => {
3415
3580
  if (targetError) onError?.(targetError);
3416
3581
  }, [onError, targetError]);
3417
3582
  const {
@@ -3431,12 +3596,12 @@ function FilloProvider({
3431
3596
  });
3432
3597
  const [resyncWarning, setResyncWarning] = useState7(null);
3433
3598
  const rawSchema = codeForm ? resolvedSchema && !devChrome ? resolvedSchema : codeForm.schema : form;
3434
- const normalized = useMemo6(() => normalizeFormSchema2(rawSchema), [rawSchema]);
3599
+ const normalized = useMemo7(() => normalizeFormSchema2(rawSchema), [rawSchema]);
3435
3600
  if (!normalized.ok) {
3436
3601
  throw new FilloError3(`This form could not be rendered: ${normalized.error}`, 422);
3437
3602
  }
3438
3603
  const schema = normalized.schema;
3439
- const resolveFormId = useMemo6(
3604
+ const resolveFormId = useMemo7(
3440
3605
  () => codeForm && activeClient?.key ? async () => {
3441
3606
  try {
3442
3607
  const result = await syncCodeForm3(activeClient, codeForm, { bypassCache: true });
@@ -3522,7 +3687,7 @@ function FilloProvider({
3522
3687
  const freshWarningUrl = resyncWarning ? resyncWarning.url : warningUrl;
3523
3688
  const freshUploadsAvailable = resyncWarning ? resyncWarning.uploadsAvailable : uploadsAvailable;
3524
3689
  const freshUploadFileSizeLimitMb = resyncWarning ? resyncWarning.uploadFileSizeLimitMb : uploadFileSizeLimitMb;
3525
- const chromeValue = useMemo6(
3690
+ const chromeValue = useMemo7(
3526
3691
  () => ({
3527
3692
  devChrome,
3528
3693
  renderOnly: localPreview,
@@ -3542,11 +3707,11 @@ function FilloProvider({
3542
3707
  );
3543
3708
  const resolvedStrings = resolveStrings2(strings);
3544
3709
  if (targetError) {
3545
- if (renderError) return /* @__PURE__ */ jsx9(Fragment4, { children: renderError(targetError) });
3710
+ if (renderError) return /* @__PURE__ */ jsx10(Fragment4, { children: renderError(targetError) });
3546
3711
  throw targetError;
3547
3712
  }
3548
3713
  const canonicalSyncError = syncError && !localPreview ? syncError : null;
3549
- if (canonicalSyncError && renderError) return /* @__PURE__ */ jsx9(Fragment4, { children: renderError(canonicalSyncError) });
3714
+ if (canonicalSyncError && renderError) return /* @__PURE__ */ jsx10(Fragment4, { children: renderError(canonicalSyncError) });
3550
3715
  if (canonicalSyncError && !devChrome) return null;
3551
3716
  if (codeFormSyncing && !localPreview && !devChrome) return null;
3552
3717
  const notPublished = Boolean(codeForm) && syncedStatus === "draft" && !localPreview;
@@ -3558,9 +3723,9 @@ function FilloProvider({
3558
3723
  void 0,
3559
3724
  syncedAcceptingReason ?? "form_not_accepting"
3560
3725
  );
3561
- return renderError ? /* @__PURE__ */ jsx9(Fragment4, { children: renderError(error) }) : null;
3726
+ return renderError ? /* @__PURE__ */ jsx10(Fragment4, { children: renderError(error) }) : null;
3562
3727
  }
3563
- 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 }) }) }) }) });
3728
+ return /* @__PURE__ */ jsx10(FilloContext.Provider, { value: api, children: /* @__PURE__ */ jsx10(FilloChromeContext.Provider, { value: chromeValue, children: /* @__PURE__ */ jsx10(FilloStringsContext.Provider, { value: resolvedStrings, children: /* @__PURE__ */ jsx10(FilloAppearanceContext.Provider, { value: appearance, children: /* @__PURE__ */ jsx10(FilloInstanceIdContext.Provider, { value: instanceId, children }) }) }) }) });
3564
3729
  }
3565
3730
 
3566
3731
  // src/index.ts