@formancy/react 0.9.0 → 0.10.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -1 +1 @@
1
- {"version":3,"file":"index.d.mts","names":[],"sources":["../src/context.tsx","../src/use-field.ts","../src/use-repeater.ts","../src/use-wizard.ts","../src/use-submit.ts","../src/go-to-field.ts","../src/fields/internals.tsx","../src/form.tsx","../src/error-summary.tsx","../src/rich-text.tsx","../src/options-source.ts","../src/scanning.ts","../src/uploads.ts","../src/rich-text-editor.ts","../src/resume-notice.tsx"],"mappings":";;;;;wBAsBgB,mBACd,QACA,OACA;EAEA,QAAQ;;;;;;;EAOR,QAAQ;EACR,WAAW;oBACZ,IAAA;wBAsBe,iBAAiB;;;;;;;;;wBAkBjB,eAAe;;;UCvEd,qBAAqB;EACpC,SAAS;EACT;;EAEA,cAAc;;EAEd;IAAc;IAAY;;;EAE1B;IAAc;;;;;;;;;;;;;wBAaA,SAAS,eAAe,OAAO;;;UCrB9B;EACf;;EAEA;;EAEA;;EAEA;EACA;EACA,UAAU;;EAEV,QAAQ,cAAc;;;;;;;wBAQR,YAAY,eAAe,OAAO;;;UCrBjC;EACf;EACA;;;EAGA;EACA;;EAEA,QAAQ;EACR;;EAEA,KAAK;;;;;;;wBAQS,aAAa;;;;;;;;;;;wBCTb;EAAqB;EAAa,QAAQ;;;;;;;;;;;wBCD1C,iBAAiB;;;;;;;;;;;;;;;;UCYhB;EACf;EACA;;KAGU,iBAAiB,cAAc;UAE1B;EACf,SAAS,QAAQ,OAAO,WAAW;EACnC,SAAS,eAAe;;UAGT;EACf;EACA,QAAQ;;EAER;;UAGe;;;;;;;EAOf,SAAS;EACT,WAAW;;;;;;;EAOX;EACA;;;;;EAKA;EACA,YAAY,SAAS;;;;;;;;;;;;;;;;;;;;;;;;;wBCPP,aAAa,OAAO,oCAAiB,IAAA;;;UCpDpC;EACf,SAAS;;;;;;;;;;;wBAYK,eAAe,UAAU,oCAAiB,IAAA;;;;;;;;;;;;;;;;;;;;wBCG1C,WAAW;EAAY;IAAmB;;;;;;;;;;UCZzC;;;;;EAKf;;EAEA;;EAEA;;EAEA;;EAEA;;;;;;;;;EASA;;;;;;;;EAQA;;EAEA,QAAQ;;;;;;;;;;;UAYO;;;;;;;;;;;;;EAaf,QAAQ,SAAS,iBAAiB,QAAQ;;EAE1C;;;;;;EAMA;;EAEA;;;;;;;;;;;;;KAcU,iBAAiB,SAAS,eAAe;qBAIxC,wCAAsB,SAAA,SAAA,eAAA;;;;;;;;;wBAUnB,qBAAqB;;;;;;;;;;;;UC9FpB;;EAEf;;EAEA;;;;;;;;;;;;;;;;;;;KAoBU,WAAW,SAAS,gBAAgB;qBAInC,iCAAe,SAAA;;;;;;;;wBASZ,cAAc;;;;;;;;;;;UC/Bb;;;;;EAKf;;EAEA,QAAQ;;EAER,WAAW,cAAc;;;;;;;;;;;;KAaf,YAAY,MAAM,MAAM,SAAS,kBAAkB,QAAQ;qBAI1D,kCAAgB,SAAA;;;;;;;;;wBAUb,eAAe;;;;;;;;;;;;;;;;;;;;;UChCd;;EAEf;;EAEA,WAAW;;;;;;;;;;;;;EAaX,MAAM,SAAS,aAAa;;EAE5B,WAAW,SAAS;EACpB;;UAGe;;WAEN,SAAS;;WAET;;WAEA,WAAW;WACX;;;;;;;;;;WAUA,YAAY,SAAS;;KAGpB,yBAAyB,OAAO,wBAAwB;qBAIvD,wCAAsB,SAAA;;;;;;;;;wBAUnB,4BAA4B;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;UC9C3B;WACN;WACA,SAAS;aAAyB;aAAuB;;;UAGnD;;WAEN,YAAY;;WAEZ,SAAS,SAAS;;wBAGb,eAAe,WAAW,UAAU,oBAAoB"}
1
+ {"version":3,"file":"index.d.mts","names":[],"sources":["../src/context.tsx","../src/use-field.ts","../src/use-repeater.ts","../src/use-wizard.ts","../src/use-submit.ts","../src/go-to-field.ts","../src/fields/internals.tsx","../src/form.tsx","../src/error-summary.tsx","../src/rich-text.tsx","../src/options-source.ts","../src/scanning.ts","../src/uploads.ts","../src/rich-text-editor.ts","../src/resume-notice.tsx"],"mappings":";;;;;wBAsBgB,mBACd,QACA,OACA;EAEA,QAAQ;;;;;;;EAOR,QAAQ;EACR,WAAW;oBACZ,IAAA;wBAsBe,iBAAiB;;;;;;;;;wBAkBjB,eAAe;;;UCvEd,qBAAqB;EACpC,SAAS;EACT;;EAEA,cAAc;;EAEd;IAAc;IAAY;;;EAE1B;IAAc;;;;;;;;;;;;;wBAaA,SAAS,eAAe,OAAO;;;UCrB9B;EACf;;EAEA;;EAEA;;EAEA;EACA;EACA,UAAU;;EAEV,QAAQ,cAAc;;;;;;;wBAQR,YAAY,eAAe,OAAO;;;UCrBjC;EACf;EACA;;;EAGA;EACA;;EAEA,QAAQ;EACR;;EAEA,KAAK;;;;;;;wBAQS,aAAa;;;;;;;;;;;wBCTb;EAAqB;EAAa,QAAQ;;;;;;;;;;;wBCD1C,iBAAiB;;;;;;;;;;;;;;;;UCYhB;EACf;EACA;;KAGU,iBAAiB,cAAc;UAE1B;EACf,SAAS,QAAQ,OAAO,WAAW;EACnC,SAAS,eAAe;;UAGT;EACf;EACA,QAAQ;;EAER;;UAGe;;;;;;;EAOf,SAAS;EACT,WAAW;;;;;;;EAOX;EACA;;;;;EAKA;EACA,YAAY,SAAS;;;;;;;;;;;;;;;;;;;;;;;;;wBCNP,aAAa,OAAO,oCAAiB,IAAA;;;UCrDpC;EACf,SAAS;;;;;;;;;;;wBAYK,eAAe,UAAU,oCAAiB,IAAA;;;;;;;;;;;;;;;;;;;;wBCG1C,WAAW;EAAY;IAAmB;;;;;;;;;;UCZzC;;;;;EAKf;;EAEA;;EAEA;;EAEA;;EAEA;;;;;;;;;EASA;;;;;;;;EAQA;;EAEA,QAAQ;;;;;;;;;;;UAYO;;;;;;;;;;;;;EAaf,QAAQ,SAAS,iBAAiB,QAAQ;;EAE1C;;;;;;EAMA;;EAEA;;;;;;;;;;;;;KAcU,iBAAiB,SAAS,eAAe;qBAIxC,wCAAsB,SAAA,SAAA,eAAA;;;;;;;;;wBAUnB,qBAAqB;;;;;;;;;;;;UC9FpB;;EAEf;;EAEA;;;;;;;;;;;;;;;;;;;KAoBU,WAAW,SAAS,gBAAgB;qBAInC,iCAAe,SAAA;;;;;;;;wBASZ,cAAc;;;;;;;;;;;UC/Bb;;;;;EAKf;;EAEA,QAAQ;;EAER,WAAW,cAAc;;;;;;;;;;;;KAaf,YAAY,MAAM,MAAM,SAAS,kBAAkB,QAAQ;qBAI1D,kCAAgB,SAAA;;;;;;;;;wBAUb,eAAe;;;;;;;;;;;;;;;;;;;;;UChCd;;EAEf;;EAEA,WAAW;;;;;;;;;;;;;EAaX,MAAM,SAAS,aAAa;;EAE5B,WAAW,SAAS;EACpB;;UAGe;;WAEN,SAAS;;WAET;;WAEA,WAAW;WACX;;;;;;;;;;WAUA,YAAY,SAAS;;KAGpB,yBAAyB,OAAO,wBAAwB;qBAIvD,wCAAsB,SAAA;;;;;;;;;wBAUnB,4BAA4B;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;UC9C3B;WACN;WACA,SAAS;aAAyB;aAAuB;;;UAGnD;;WAEN,YAAY;;WAEZ,SAAS,SAAS;;wBAGb,eAAe,WAAW,UAAU,oBAAoB"}
package/dist/index.mjs CHANGED
@@ -1,6 +1,6 @@
1
1
  import { Fragment, createContext, useCallback, useContext, useEffect, useId, useLayoutEffect, useMemo, useRef, useState, useSyncExternalStore } from "react";
2
- import { cancelUploads, fieldUploads, formatPath, inOptionOrder, parsePath, placedGroup, placedPage, thumbnailSize, ticked } from "@formancy/core";
3
- import { answersToReview, countSentence, createFormText, errorSentence, optionsStatus } from "@formancy/core/words";
2
+ import { cancelUploads, fieldUploads, formatPath, hasReadOnlyControl, inOptionOrder, parsePath, placedGroup, placedPage, thumbnailSize, ticked } from "@formancy/core";
3
+ import { answerInWords, answersToReview, countSentence, createFormText, errorSentence, optionsStatus } from "@formancy/core/words";
4
4
  import { LAYOUT_LEAF_KINDS, acceptRemoteAnswer, answerFromText, applyRichCommand, capRemoteAnswer, datagridColumns, editMasked, formatMasked, isImageSource, layoutChildren, layoutNodeShows, maskIsNumeric, maskPlaceholder, narrowOptionsByLabel, parseRichText, resolveText, resolvedLocale } from "@formancy/spec";
5
5
  import { Fragment as Fragment$1, jsx, jsxs } from "react/jsx-runtime";
6
6
  import { flushSync } from "react-dom";
@@ -464,7 +464,10 @@ function Review({ onChange, children }) {
464
464
  "data-formancy-part": "review-answer",
465
465
  children: [
466
466
  /* @__PURE__ */ jsx("dt", { children: answer.label }),
467
- /* @__PURE__ */ jsx("dd", { children: answer.said }),
467
+ /* @__PURE__ */ jsx("dd", {
468
+ ...answer.answered ? {} : { "data-formancy-part": "review-unanswered" },
469
+ children: answer.said
470
+ }),
468
471
  /* @__PURE__ */ jsx("dd", { children: /* @__PURE__ */ jsxs("button", {
469
472
  type: "button",
470
473
  onClick: () => onChange(answer),
@@ -556,7 +559,8 @@ function useSourcedOptions(field, query, enabled = true) {
556
559
  const debounceMs = source?.debounceMs ?? DEBOUNCE_MS;
557
560
  const minQueryLength = source?.minQueryLength ?? MIN_QUERY;
558
561
  const maxRows = source?.maxRows ?? MAX_ROWS;
559
- const stored = typeof field.value === "string" && field.value !== "" ? field.value : void 0;
562
+ const stored = storedValues(field.value);
563
+ const storedKey = stored.join("\0");
560
564
  const path = field.path;
561
565
  const disabled = field.disabled;
562
566
  const asked = useRef(0);
@@ -628,18 +632,19 @@ function useSourcedOptions(field, query, enabled = true) {
628
632
  ]);
629
633
  const askedFor = useRef(/* @__PURE__ */ new Set());
630
634
  useEffect(() => {
631
- if (!enabled || source === void 0 || stored === void 0) return;
632
- if (named.has(stored) || askedFor.current.has(stored)) return;
633
- askedFor.current.add(stored);
635
+ if (!enabled || source === void 0 || storedKey === "") return;
636
+ const unnamed = storedKey.split("\0").filter((value) => !named.has(value) && !askedFor.current.has(value));
637
+ if (unnamed.length === 0) return;
638
+ for (const value of unnamed) askedFor.current.add(value);
634
639
  const controller = new AbortController();
635
640
  source.resolve({
636
641
  kind: "labels",
637
642
  source: name ?? "",
638
643
  path,
639
644
  query: "",
640
- values: [stored],
645
+ values: unnamed,
641
646
  locale: engine.locale(),
642
- limit: 1,
647
+ limit: unnamed.length,
643
648
  signal: controller.signal
644
649
  }).then((answer) => {
645
650
  const accepted = acceptRemoteAnswer(answer);
@@ -656,7 +661,7 @@ function useSourcedOptions(field, query, enabled = true) {
656
661
  source,
657
662
  name,
658
663
  path,
659
- stored,
664
+ storedKey,
660
665
  named,
661
666
  engine
662
667
  ]);
@@ -667,13 +672,11 @@ function useSourcedOptions(field, query, enabled = true) {
667
672
  })),
668
673
  remote: null
669
674
  };
670
- const offered = [...rows];
671
- if (stored !== void 0 && !offered.some((row) => row.value === stored)) offered.unshift({
672
- value: stored,
673
- label: named.get(stored) ?? stored
674
- });
675
675
  return {
676
- options: offered,
676
+ options: [...stored.filter((value) => !rows.some((row) => row.value === value)).map((value) => ({
677
+ value,
678
+ label: named.get(value) ?? value
679
+ })), ...rows],
677
680
  remote: {
678
681
  unavailable: source === void 0,
679
682
  busy,
@@ -684,6 +687,11 @@ function useSourcedOptions(field, query, enabled = true) {
684
687
  }
685
688
  };
686
689
  }
690
+ function storedValues(value) {
691
+ if (typeof value === "string") return value === "" ? [] : [value];
692
+ if (!Array.isArray(value)) return [];
693
+ return value.filter((item) => typeof item === "string" && item !== "");
694
+ }
687
695
  //#endregion
688
696
  //#region src/uploads.ts
689
697
  const UploaderContext = createContext(void 0);
@@ -1034,6 +1042,24 @@ function RadioGroupField({ path, label }) {
1034
1042
  });
1035
1043
  }
1036
1044
  //#endregion
1045
+ //#region src/fields/computed-answer.tsx
1046
+ function ComputedAnswerField({ path, label }) {
1047
+ const field = useField(path);
1048
+ const engine = useFormEngine();
1049
+ const text = useFormText();
1050
+ return /* @__PURE__ */ jsx(FieldShell, {
1051
+ path,
1052
+ field,
1053
+ label,
1054
+ children: /* @__PURE__ */ jsx("input", {
1055
+ type: "text",
1056
+ readOnly: true,
1057
+ ...field.controlProps,
1058
+ value: answerInWords(engine.schema(), field.def, field.value, text, engine.locale())
1059
+ })
1060
+ });
1061
+ }
1062
+ //#endregion
1037
1063
  //#region src/fields/file-field.tsx
1038
1064
  function FileField({ path, label }) {
1039
1065
  const field = useField(path);
@@ -1474,14 +1500,30 @@ function SelectBoxesSwitch(props) {
1474
1500
  function TagPickerField({ path, label }) {
1475
1501
  const field = useField(path);
1476
1502
  const text = useFormText();
1477
- const options = useResolvedOptions(field);
1478
1503
  const chosen = Array.isArray(field.value) ? field.value.map(String) : [];
1479
1504
  const [query, setQuery] = useState("");
1480
1505
  const [open, setOpen] = useState(false);
1481
1506
  const listboxId = `${field.ids.control}:listbox`;
1507
+ const written = useResolvedOptions(field);
1508
+ const sourced = useSourcedOptions({
1509
+ ...field,
1510
+ path
1511
+ }, query);
1512
+ const remote = sourced.remote;
1513
+ const options = remote === null ? written : sourced.options;
1482
1514
  const remaining = options.filter((option) => !chosen.includes(option.value));
1483
- const matches = narrowOptionsByLabel(remaining, query);
1515
+ const matches = remote === null ? narrowOptionsByLabel(remaining, query) : remaining;
1484
1516
  const expanded = open && !field.disabled && matches.length > 0;
1517
+ const said = remote === null ? "" : optionsStatus(text, {
1518
+ control: "typeahead",
1519
+ open: open && !field.disabled,
1520
+ disabled: field.disabled,
1521
+ failed: remote.failed,
1522
+ busy: remote.busy,
1523
+ needs: remote.needs,
1524
+ query,
1525
+ completeness: remote.completeness
1526
+ });
1485
1527
  const add = (value) => {
1486
1528
  field.setValue(ticked(field.def, chosen, value, true));
1487
1529
  setQuery("");
@@ -1491,92 +1533,139 @@ function TagPickerField({ path, label }) {
1491
1533
  field.setValue(chosen.filter((candidate) => candidate !== value));
1492
1534
  };
1493
1535
  const labelFor = (value) => options.find((option) => option.value === value)?.label ?? value;
1536
+ if (remote?.unavailable === true) return /* @__PURE__ */ jsx(FieldShell, {
1537
+ path,
1538
+ field,
1539
+ label,
1540
+ children: /* @__PURE__ */ jsx("p", {
1541
+ "data-formancy-part": "options-unavailable",
1542
+ children: text("options.unavailable", { source: field.def.optionsSource ?? "" })
1543
+ })
1544
+ });
1494
1545
  return /* @__PURE__ */ jsx(FieldShell, {
1495
1546
  path,
1496
1547
  field,
1497
1548
  label,
1498
1549
  children: /* @__PURE__ */ jsxs("div", {
1499
1550
  "data-formancy-part": "tagpicker",
1500
- children: [chosen.length === 0 ? null : /* @__PURE__ */ jsx("ul", {
1501
- "data-formancy-part": "tagpicker-chips",
1502
- "aria-label": text("tagpicker.chosen", { label }),
1503
- children: chosen.map((value) => /* @__PURE__ */ jsxs("li", {
1504
- "data-formancy-part": "tagpicker-chip",
1505
- children: [labelFor(value), /* @__PURE__ */ jsx("button", {
1506
- type: "button",
1507
- "data-formancy-part": "tagpicker-remove",
1508
- "aria-label": text("tagpicker.remove", { option: labelFor(value) }),
1509
- disabled: field.disabled,
1510
- onClick: () => remove(value),
1511
- children: "×"
1512
- })]
1513
- }, value))
1514
- }), /* @__PURE__ */ jsxs("div", {
1515
- "data-formancy-part": "tagpicker-anchor",
1516
- children: [/* @__PURE__ */ jsx("input", {
1517
- type: "text",
1518
- role: "combobox",
1519
- ...field.controlProps,
1520
- "data-formancy-part": "tagpicker-input",
1521
- autoComplete: "off",
1522
- "aria-expanded": expanded,
1523
- "aria-controls": listboxId,
1524
- "aria-autocomplete": "list",
1525
- value: query,
1526
- onChange: (event) => {
1527
- setQuery(event.target.value);
1528
- setOpen(true);
1529
- },
1530
- onClick: () => setOpen(true),
1531
- onKeyDown: (event) => {
1532
- if (event.key === "Escape") {
1551
+ children: [
1552
+ chosen.length === 0 ? null : /* @__PURE__ */ jsx("ul", {
1553
+ "data-formancy-part": "tagpicker-chips",
1554
+ "aria-label": text("tagpicker.chosen", { label }),
1555
+ children: chosen.map((value) => /* @__PURE__ */ jsxs("li", {
1556
+ "data-formancy-part": "tagpicker-chip",
1557
+ children: [labelFor(value), /* @__PURE__ */ jsx("button", {
1558
+ type: "button",
1559
+ "data-formancy-part": "tagpicker-remove",
1560
+ "aria-label": text("tagpicker.remove", { option: labelFor(value) }),
1561
+ disabled: field.disabled,
1562
+ onClick: () => remove(value),
1563
+ children: "×"
1564
+ })]
1565
+ }, value))
1566
+ }),
1567
+ /* @__PURE__ */ jsxs("div", {
1568
+ "data-formancy-part": "tagpicker-anchor",
1569
+ children: [/* @__PURE__ */ jsx("input", {
1570
+ type: "text",
1571
+ role: "combobox",
1572
+ ...field.controlProps,
1573
+ "data-formancy-part": "tagpicker-input",
1574
+ autoComplete: "off",
1575
+ "aria-expanded": expanded,
1576
+ "aria-controls": listboxId,
1577
+ "aria-autocomplete": "list",
1578
+ "aria-busy": remote?.busy === true ? true : void 0,
1579
+ value: query,
1580
+ onChange: (event) => {
1581
+ setQuery(event.target.value);
1582
+ setOpen(true);
1583
+ },
1584
+ onClick: () => setOpen(true),
1585
+ onKeyDown: (event) => {
1586
+ if (event.key === "Escape") {
1587
+ setOpen(false);
1588
+ return;
1589
+ }
1590
+ if (event.key === "Enter" && expanded && matches[0] !== void 0) {
1591
+ event.preventDefault();
1592
+ add(matches[0].value);
1593
+ return;
1594
+ }
1595
+ if (event.key === "Backspace" && query === "" && chosen.length > 0) remove(chosen[chosen.length - 1]);
1596
+ },
1597
+ onBlur: () => {
1533
1598
  setOpen(false);
1534
- return;
1535
- }
1536
- if (event.key === "Enter" && expanded && matches[0] !== void 0) {
1537
- event.preventDefault();
1538
- add(matches[0].value);
1539
- return;
1599
+ field.touch();
1540
1600
  }
1541
- if (event.key === "Backspace" && query === "" && chosen.length > 0) remove(chosen[chosen.length - 1]);
1542
- },
1543
- onBlur: () => {
1544
- setOpen(false);
1545
- field.touch();
1546
- }
1547
- }), /* @__PURE__ */ jsx("ul", {
1548
- id: listboxId,
1549
- role: "listbox",
1550
- "aria-label": text("options.suggestions", { label }),
1551
- "data-formancy-part": "tagpicker-listbox",
1552
- hidden: !expanded,
1553
- children: matches.map((option) => /* @__PURE__ */ jsx("li", {
1554
- role: "option",
1555
- "data-formancy-part": "tagpicker-option",
1556
- "aria-selected": false,
1557
- onMouseDown: (event) => event.preventDefault(),
1558
- onClick: () => add(option.value),
1559
- children: option.label
1560
- }, option.value))
1561
- })]
1562
- })]
1601
+ }), /* @__PURE__ */ jsx("ul", {
1602
+ id: listboxId,
1603
+ role: "listbox",
1604
+ "aria-label": text("options.suggestions", { label }),
1605
+ "data-formancy-part": "tagpicker-listbox",
1606
+ hidden: !expanded,
1607
+ children: matches.map((option) => /* @__PURE__ */ jsx("li", {
1608
+ role: "option",
1609
+ "data-formancy-part": "tagpicker-option",
1610
+ "aria-selected": false,
1611
+ onMouseDown: (event) => event.preventDefault(),
1612
+ onClick: () => add(option.value),
1613
+ children: option.label
1614
+ }, option.value))
1615
+ })]
1616
+ }),
1617
+ remote === null ? null : /* @__PURE__ */ jsx("p", {
1618
+ role: "status",
1619
+ "data-formancy-part": "tagpicker-status",
1620
+ "data-state": remote.failed ? "failed" : void 0,
1621
+ children: said
1622
+ })
1623
+ ]
1563
1624
  })
1564
1625
  });
1565
1626
  }
1566
1627
  function SelectBoxesField({ path, label }) {
1567
1628
  const field = useField(path);
1568
- const options = useResolvedOptions(field);
1629
+ const text = useFormText();
1630
+ const written = useResolvedOptions(field);
1631
+ const sourced = useSourcedOptions({
1632
+ ...field,
1633
+ path
1634
+ }, "");
1635
+ const remote = sourced.remote;
1636
+ const options = remote === null ? written : sourced.options;
1569
1637
  const showError = field.touched && field.errors.length > 0;
1570
1638
  const chosen = Array.isArray(field.value) ? field.value : [];
1639
+ const group = useRef(null);
1640
+ const again = useRef(null);
1641
+ const [retrying, setRetrying] = useState(false);
1642
+ const settled = remote !== null && !remote.busy && !remote.failed;
1643
+ useEffect(() => {
1644
+ if (!retrying || !settled) return;
1645
+ setRetrying(false);
1646
+ if (document.activeElement === again.current) group.current?.querySelector("input[type=\"checkbox\"]")?.focus();
1647
+ }, [retrying, settled]);
1648
+ const said = remote === null ? "" : optionsStatus(text, {
1649
+ control: "ticks",
1650
+ open: true,
1651
+ disabled: field.disabled,
1652
+ failed: remote.failed,
1653
+ busy: remote.busy,
1654
+ needs: remote.needs,
1655
+ query: "",
1656
+ completeness: remote.completeness
1657
+ });
1571
1658
  const toggle = (value, on) => {
1572
1659
  field.setValue(ticked(field.def, chosen, value, on));
1573
1660
  };
1574
1661
  return /* @__PURE__ */ jsxs("fieldset", {
1662
+ ref: group,
1575
1663
  id: field.ids.control,
1576
1664
  "data-formancy-part": "field",
1577
1665
  "data-formancy-field-path": path,
1578
1666
  "data-state": showError ? "invalid" : "valid",
1579
1667
  "aria-describedby": field.controlProps["aria-describedby"],
1668
+ "aria-busy": remote?.busy === true ? true : void 0,
1580
1669
  children: [
1581
1670
  /* @__PURE__ */ jsx("legend", {
1582
1671
  "data-formancy-part": "label",
@@ -1584,6 +1673,10 @@ function SelectBoxesField({ path, label }) {
1584
1673
  }),
1585
1674
  /* @__PURE__ */ jsx(RequiredHint, { field }),
1586
1675
  /* @__PURE__ */ jsx(Description, { field }),
1676
+ remote?.unavailable === true ? /* @__PURE__ */ jsx("p", {
1677
+ "data-formancy-part": "options-unavailable",
1678
+ children: text("options.unavailable", { source: field.def.optionsSource ?? "" })
1679
+ }) : null,
1587
1680
  options.map((option) => {
1588
1681
  const optionId = `${field.ids.control}:${option.value}`;
1589
1682
  return /* @__PURE__ */ jsxs("span", {
@@ -1603,6 +1696,22 @@ function SelectBoxesField({ path, label }) {
1603
1696
  })]
1604
1697
  }, option.value);
1605
1698
  }),
1699
+ remote === null || remote.unavailable ? null : /* @__PURE__ */ jsx("p", {
1700
+ role: "status",
1701
+ "data-formancy-part": "checkbox-status",
1702
+ "data-state": remote.failed ? "failed" : void 0,
1703
+ children: said
1704
+ }),
1705
+ remote !== null && (remote.failed || retrying) && !field.disabled ? /* @__PURE__ */ jsx("button", {
1706
+ ref: again,
1707
+ type: "button",
1708
+ "data-formancy-part": "checkbox-retry",
1709
+ onClick: () => {
1710
+ setRetrying(true);
1711
+ remote.retry();
1712
+ },
1713
+ children: text("options.retry")
1714
+ }) : null,
1606
1715
  showError ? /* @__PURE__ */ jsx(ErrorText, { field }) : null
1607
1716
  ]
1608
1717
  });
@@ -2324,7 +2433,7 @@ function FieldList({ labels, registry, page, layout }) {
2324
2433
  function FieldSlot({ path, fallbackLabel, registry }) {
2325
2434
  const field = useField(path);
2326
2435
  if (!field.visible) return null;
2327
- const Component = registry?.byPath?.[path] ?? registry?.byType?.[field.type] ?? DEFAULT_COMPONENTS[field.type];
2436
+ const Component = registry?.byPath?.[path] ?? (field.readOnly && !hasReadOnlyControl(field.def) ? ComputedAnswerField : void 0) ?? registry?.byType?.[field.type] ?? DEFAULT_COMPONENTS[field.type];
2328
2437
  if (Component === null) return null;
2329
2438
  const label = field.label ?? fallbackLabel ?? path;
2330
2439
  return /* @__PURE__ */ jsx(Component, {