@marktiderman/genesis-forms-web 0.4.0 → 0.6.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
@@ -3,8 +3,8 @@ import { createContext, useState, useEffect, useRef, useId, useContext, useMemo,
3
3
  import { Button } from '@marktiderman/genesis-ui/button';
4
4
  import { Progress } from '@marktiderman/genesis-ui/progress';
5
5
  import { cn } from '@marktiderman/genesis-ui/utils';
6
- import { ratingDisplay, choiceOptions, otherOption, numberLimits, dateLimits, listLimits, tableColumns, tableLimits, parseTimestamp, formatTimestamp, formatCalculated, photoLimits, photoRefs, getFieldType, scaleLabels, OTHER_MAX_LENGTH, photoCountMessage, isPhotoRef, photoTypeMessage, photoSizeMessage, formProgress } from '@marktiderman/genesis-forms';
7
- import { createRendererRegistry, useFormSelector, useForm, FormRuntime, useFormContext, FieldRenderer } from '@marktiderman/genesis-forms-react';
6
+ import { ratingDisplay, numberLimits, dateLimits, listLimits, tableColumns, tableLimits, parseTimestamp, formatTimestamp, formatCalculated, photoLimits, photoRefs, getFieldType, scaleLabels, isSearchable, otherOption, choiceOptions, searchOptions, OTHER_MAX_LENGTH, photoCountMessage, isPhotoRef, photoTypeMessage, photoSizeMessage, respondentPath, formProgress } from '@marktiderman/genesis-forms';
7
+ import { createRendererRegistry, useFormSelector, useChoiceOptions, useForm, FormRuntime, useFormContext, FieldRenderer } from '@marktiderman/genesis-forms-react';
8
8
  import { Checkbox } from '@marktiderman/genesis-ui/checkbox';
9
9
  import { Input } from '@marktiderman/genesis-ui/input';
10
10
  import { Label } from '@marktiderman/genesis-ui/label';
@@ -15,6 +15,7 @@ import { jsx, jsxs, Fragment } from 'react/jsx-runtime';
15
15
  import { Stack } from '@marktiderman/genesis-ui/layout';
16
16
  import { SortableList } from '@marktiderman/genesis-ui/sortable-list';
17
17
  import { Spinner } from '@marktiderman/genesis-ui/spinner';
18
+ import { Command, CommandInput, CommandList, CommandEmpty, CommandItem } from '@marktiderman/genesis-ui/command';
18
19
 
19
20
  // src/Form.tsx
20
21
  var FormPresentationContext = createContext({ presentation: "embedded", layout: "classic" });
@@ -1004,6 +1005,127 @@ function PhotoField(props) {
1004
1005
  ] }) });
1005
1006
  }
1006
1007
  var photoRenderers = [{ type: "photo", Component: PhotoField }];
1008
+ function OptionImage({ src, large }) {
1009
+ return /* @__PURE__ */ jsx(
1010
+ "img",
1011
+ {
1012
+ src,
1013
+ alt: "",
1014
+ loading: "lazy",
1015
+ "data-gf-option-image": "",
1016
+ className: cn("shrink-0 rounded-md bg-muted object-cover", large ? "size-12" : "size-10")
1017
+ }
1018
+ );
1019
+ }
1020
+ function OptionText({ label, description }) {
1021
+ return /* @__PURE__ */ jsxs("span", { className: "flex min-w-0 flex-1 flex-col", children: [
1022
+ /* @__PURE__ */ jsx("span", { children: label }),
1023
+ description ? /* @__PURE__ */ jsx("span", { className: "text-sm text-muted-foreground", "data-gf-option-description": "", children: description }) : null
1024
+ ] });
1025
+ }
1026
+ function CheckMark({ on }) {
1027
+ return /* @__PURE__ */ jsx(
1028
+ "svg",
1029
+ {
1030
+ viewBox: "0 0 24 24",
1031
+ fill: "none",
1032
+ stroke: "currentColor",
1033
+ strokeWidth: "2.5",
1034
+ strokeLinecap: "round",
1035
+ strokeLinejoin: "round",
1036
+ "aria-hidden": "true",
1037
+ className: cn("size-4 shrink-0 text-primary", on ? "opacity-100" : "opacity-0"),
1038
+ children: /* @__PURE__ */ jsx("path", { d: "M20 6 9 17l-5-5" })
1039
+ }
1040
+ );
1041
+ }
1042
+ var SEARCH_OTHER_VALUE = "\0other";
1043
+ function SearchableOptions({
1044
+ props,
1045
+ options,
1046
+ selected,
1047
+ multiple,
1048
+ otherLabel,
1049
+ otherOn,
1050
+ onPick
1051
+ }) {
1052
+ const { binding, inputId } = props;
1053
+ const [query, setQuery] = useState("");
1054
+ const { presentation } = useFormPresentation();
1055
+ const page = presentation === "page";
1056
+ const shown = searchOptions(options, query);
1057
+ const chosen = options.filter((o) => selected.includes(o.value));
1058
+ const labelOf = (o) => o.label ?? o.value;
1059
+ return /* @__PURE__ */ jsxs(
1060
+ "div",
1061
+ {
1062
+ className: "space-y-2",
1063
+ "data-gf-searchable": "",
1064
+ onKeyDown: (e) => {
1065
+ if (e.key === "Enter" && e.target.tagName === "INPUT") e.preventDefault();
1066
+ },
1067
+ children: [
1068
+ /* @__PURE__ */ jsxs(Command, { shouldFilter: false, label: binding.label, className: "rounded-md border border-input bg-background", children: [
1069
+ /* @__PURE__ */ jsx(
1070
+ CommandInput,
1071
+ {
1072
+ id: inputId,
1073
+ "data-gf-focus": "",
1074
+ value: query,
1075
+ onValueChange: setQuery,
1076
+ placeholder: "Type to search",
1077
+ "aria-label": `Search: ${binding.label}`,
1078
+ "aria-invalid": binding.error ? true : void 0,
1079
+ "aria-required": binding.field.required || void 0,
1080
+ className: cn(page && "text-base")
1081
+ }
1082
+ ),
1083
+ /* @__PURE__ */ jsxs(CommandList, { className: "max-h-72", "aria-multiselectable": multiple || void 0, children: [
1084
+ /* @__PURE__ */ jsx(CommandEmpty, { children: "No matches." }),
1085
+ shown.map((o) => {
1086
+ const on = selected.includes(o.value);
1087
+ return /* @__PURE__ */ jsxs(
1088
+ CommandItem,
1089
+ {
1090
+ value: o.value,
1091
+ keywords: [labelOf(o)],
1092
+ "aria-checked": on,
1093
+ onSelect: () => onPick(o.value),
1094
+ className: cn("min-h-11 gap-3 text-base", page && "min-h-12"),
1095
+ "data-gf-option": o.value,
1096
+ children: [
1097
+ /* @__PURE__ */ jsx(CheckMark, { on }),
1098
+ o.image ? /* @__PURE__ */ jsx(OptionImage, { src: o.image }) : null,
1099
+ /* @__PURE__ */ jsx(OptionText, { label: labelOf(o), description: o.description })
1100
+ ]
1101
+ },
1102
+ o.value
1103
+ );
1104
+ }),
1105
+ otherLabel ? /* @__PURE__ */ jsxs(
1106
+ CommandItem,
1107
+ {
1108
+ value: SEARCH_OTHER_VALUE,
1109
+ "aria-checked": !!otherOn,
1110
+ onSelect: () => onPick(SEARCH_OTHER_VALUE),
1111
+ className: cn("min-h-11 gap-3 text-base", page && "min-h-12"),
1112
+ children: [
1113
+ /* @__PURE__ */ jsx(CheckMark, { on: !!otherOn }),
1114
+ /* @__PURE__ */ jsx(OptionText, { label: otherLabel })
1115
+ ]
1116
+ }
1117
+ ) : null
1118
+ ] })
1119
+ ] }),
1120
+ chosen.length ? /* @__PURE__ */ jsxs("p", { className: "text-sm text-muted-foreground", "aria-live": "polite", "data-gf-chosen": "", children: [
1121
+ multiple ? "Chosen" : "Your choice",
1122
+ ": ",
1123
+ chosen.map(labelOf).join(", ")
1124
+ ] }) : null
1125
+ ]
1126
+ }
1127
+ );
1128
+ }
1007
1129
  function groupAria(props) {
1008
1130
  return {
1009
1131
  "aria-labelledby": `${props.inputId}-label`,
@@ -1278,8 +1400,19 @@ function useChoiceLook(inputId, count) {
1278
1400
  } : void 0
1279
1401
  };
1280
1402
  }
1281
- function ChoiceContent({ look, index, label }) {
1282
- if (!look.pageFocus) return /* @__PURE__ */ jsx("span", { children: label });
1403
+ function ChoiceContent({
1404
+ look,
1405
+ index,
1406
+ label,
1407
+ description,
1408
+ image
1409
+ }) {
1410
+ if (!look.pageFocus) {
1411
+ return /* @__PURE__ */ jsxs(Fragment, { children: [
1412
+ image ? /* @__PURE__ */ jsx(OptionImage, { src: image }) : null,
1413
+ /* @__PURE__ */ jsx(OptionText, { label, description })
1414
+ ] });
1415
+ }
1283
1416
  return /* @__PURE__ */ jsxs(Fragment, { children: [
1284
1417
  /* @__PURE__ */ jsx(
1285
1418
  "span",
@@ -1289,7 +1422,8 @@ function ChoiceContent({ look, index, label }) {
1289
1422
  children: KEYS[index] ?? ""
1290
1423
  }
1291
1424
  ),
1292
- /* @__PURE__ */ jsx("span", { className: "flex-1", children: label }),
1425
+ image ? /* @__PURE__ */ jsx(OptionImage, { src: image, large: true }) : null,
1426
+ /* @__PURE__ */ jsx(OptionText, { label, description }),
1293
1427
  /* @__PURE__ */ jsx(
1294
1428
  "svg",
1295
1429
  {
@@ -1335,13 +1469,115 @@ function useOtherText(typed) {
1335
1469
  const [text, setText] = useState(typed);
1336
1470
  return { on, setOn, text, setText };
1337
1471
  }
1472
+ function SearchableSingleField(props) {
1473
+ const { binding, onAdvance } = props;
1474
+ const options = useChoiceOptions(props.engine, binding.field.id);
1475
+ const other = otherOption(binding.field);
1476
+ const current = typeof binding.value === "string" ? binding.value : "";
1477
+ const typed = other && current && !choiceOptions(binding.field).some((o) => o.value === current) ? current : "";
1478
+ const otherText = useOtherText(typed);
1479
+ const textId = `${props.inputId}-other-text`;
1480
+ return /* @__PURE__ */ jsxs(FieldChrome, { props, children: [
1481
+ /* @__PURE__ */ jsx(
1482
+ SearchableOptions,
1483
+ {
1484
+ props,
1485
+ options,
1486
+ selected: otherText.on ? [] : [current],
1487
+ multiple: false,
1488
+ otherLabel: other?.label,
1489
+ otherOn: otherText.on,
1490
+ onPick: (v) => {
1491
+ if (v === SEARCH_OTHER_VALUE) {
1492
+ otherText.setOn(true);
1493
+ binding.setValue(otherText.text.trim() ? otherText.text : void 0);
1494
+ focusSoon(textId);
1495
+ return;
1496
+ }
1497
+ otherText.setOn(false);
1498
+ binding.setValue(v);
1499
+ onAdvance?.();
1500
+ }
1501
+ }
1502
+ ),
1503
+ other && otherText.on ? /* @__PURE__ */ jsx(
1504
+ OtherText,
1505
+ {
1506
+ id: textId,
1507
+ label: other.label,
1508
+ value: otherText.text,
1509
+ invalid: !!binding.error,
1510
+ onChange: (text) => {
1511
+ otherText.setText(text);
1512
+ binding.setValue(text.trim() ? text : void 0);
1513
+ }
1514
+ }
1515
+ ) : null
1516
+ ] });
1517
+ }
1518
+ function SearchableMultiField(props) {
1519
+ const { binding } = props;
1520
+ const options = useChoiceOptions(props.engine, binding.field.id);
1521
+ const other = otherOption(binding.field);
1522
+ const selected = Array.isArray(binding.value) ? binding.value : [];
1523
+ const known = new Set(choiceOptions(binding.field).map((o) => o.value));
1524
+ const chosen = selected.filter((v) => known.has(v));
1525
+ const otherText = useOtherText(other ? selected.find((v) => !known.has(v)) ?? "" : "");
1526
+ const textId = `${props.inputId}-other-text`;
1527
+ const commit = (picked, otherOn, text) => {
1528
+ const next = otherOn && text.trim() ? [...picked, text] : picked;
1529
+ binding.setValue(next.length ? next : void 0);
1530
+ };
1531
+ return /* @__PURE__ */ jsxs(FieldChrome, { props, children: [
1532
+ /* @__PURE__ */ jsx(
1533
+ SearchableOptions,
1534
+ {
1535
+ props,
1536
+ options,
1537
+ selected: chosen,
1538
+ multiple: true,
1539
+ otherLabel: other?.label,
1540
+ otherOn: otherText.on,
1541
+ onPick: (v) => {
1542
+ if (v === SEARCH_OTHER_VALUE) {
1543
+ const on = !otherText.on;
1544
+ otherText.setOn(on);
1545
+ commit(chosen, on, otherText.text);
1546
+ if (on) focusSoon(textId);
1547
+ return;
1548
+ }
1549
+ commit(chosen.includes(v) ? chosen.filter((x) => x !== v) : [...chosen, v], otherText.on, otherText.text);
1550
+ }
1551
+ }
1552
+ ),
1553
+ other && otherText.on ? /* @__PURE__ */ jsx(
1554
+ OtherText,
1555
+ {
1556
+ id: textId,
1557
+ label: other.label,
1558
+ value: otherText.text,
1559
+ invalid: !!binding.error,
1560
+ onChange: (text) => {
1561
+ otherText.setText(text);
1562
+ commit(chosen, true, text);
1563
+ }
1564
+ }
1565
+ ) : null
1566
+ ] });
1567
+ }
1568
+ function useSearchable(props) {
1569
+ return isSearchable(props.binding.field);
1570
+ }
1338
1571
  function SingleChoiceField(props) {
1572
+ return useSearchable(props) ? /* @__PURE__ */ jsx(SearchableSingleField, { ...props }) : /* @__PURE__ */ jsx(SingleChoiceList, { ...props });
1573
+ }
1574
+ function SingleChoiceList(props) {
1339
1575
  const { binding, inputId, onAdvance } = props;
1340
1576
  const pick = useDeliberatePick(onAdvance);
1341
- const options = choiceOptions(binding.field);
1577
+ const options = useChoiceOptions(props.engine, binding.field.id);
1342
1578
  const other = otherOption(binding.field);
1343
1579
  const current = typeof binding.value === "string" ? binding.value : "";
1344
- const typed = other && current && !options.some((o) => o.value === current) ? current : "";
1580
+ const typed = other && current && !choiceOptions(binding.field).some((o) => o.value === current) ? current : "";
1345
1581
  const otherText = useOtherText(typed);
1346
1582
  const look = useChoiceLook(inputId, options.length + (other ? 1 : 0));
1347
1583
  const textId = `${inputId}-other-text`;
@@ -1376,7 +1612,7 @@ function SingleChoiceField(props) {
1376
1612
  className: look.control
1377
1613
  }
1378
1614
  ),
1379
- /* @__PURE__ */ jsx(ChoiceContent, { look, index: i, label: o.label ?? o.value })
1615
+ /* @__PURE__ */ jsx(ChoiceContent, { look, index: i, label: o.label ?? o.value, description: o.description, image: o.image })
1380
1616
  ] }, o.value)),
1381
1617
  other ? (
1382
1618
  // No auto-advance: the respondent still has to type.
@@ -1404,11 +1640,14 @@ function SingleChoiceField(props) {
1404
1640
  ] });
1405
1641
  }
1406
1642
  function MultiChoiceField(props) {
1643
+ return useSearchable(props) ? /* @__PURE__ */ jsx(SearchableMultiField, { ...props }) : /* @__PURE__ */ jsx(MultiChoiceList, { ...props });
1644
+ }
1645
+ function MultiChoiceList(props) {
1407
1646
  const { binding, inputId } = props;
1408
1647
  const selected = Array.isArray(binding.value) ? binding.value : [];
1409
- const options = choiceOptions(binding.field);
1648
+ const options = useChoiceOptions(props.engine, binding.field.id);
1410
1649
  const other = otherOption(binding.field);
1411
- const known = new Set(options.map((o) => o.value));
1650
+ const known = new Set(choiceOptions(binding.field).map((o) => o.value));
1412
1651
  const chosen = selected.filter((v) => known.has(v));
1413
1652
  const otherText = useOtherText(other ? selected.find((v) => !known.has(v)) ?? "" : "");
1414
1653
  const look = useChoiceLook(inputId, options.length + (other ? 1 : 0));
@@ -1431,7 +1670,7 @@ function MultiChoiceField(props) {
1431
1670
  className: look.control
1432
1671
  }
1433
1672
  ),
1434
- /* @__PURE__ */ jsx(ChoiceContent, { look, index: i, label: o.label ?? o.value })
1673
+ /* @__PURE__ */ jsx(ChoiceContent, { look, index: i, label: o.label ?? o.value, description: o.description, image: o.image })
1435
1674
  ] }, o.value)),
1436
1675
  other ? /* @__PURE__ */ jsxs(Label, { htmlFor: `${inputId}-${options.length}`, className: look.row, children: [
1437
1676
  /* @__PURE__ */ jsx(
@@ -1482,12 +1721,15 @@ function controlAria(props) {
1482
1721
  };
1483
1722
  }
1484
1723
  function DropdownField(props) {
1724
+ return useSearchable(props) ? /* @__PURE__ */ jsx(SearchableSingleField, { ...props }) : /* @__PURE__ */ jsx(DropdownSelect, { ...props });
1725
+ }
1726
+ function DropdownSelect(props) {
1485
1727
  const { binding } = props;
1486
- const options = choiceOptions(binding.field);
1728
+ const options = useChoiceOptions(props.engine, binding.field.id);
1487
1729
  const other = otherOption(binding.field);
1488
1730
  const line = useLineClass();
1489
1731
  const current = typeof binding.value === "string" ? binding.value : "";
1490
- const typed = other && current && !options.some((o) => o.value === current) ? current : "";
1732
+ const typed = other && current && !choiceOptions(binding.field).some((o) => o.value === current) ? current : "";
1491
1733
  const otherText = useOtherText(typed);
1492
1734
  const value = otherText.on ? OTHER_VALUE : current;
1493
1735
  const textId = `${props.inputId}-other-text`;
@@ -1899,8 +2141,9 @@ function DefaultNavigation({
1899
2141
  /* @__PURE__ */ jsx(Button, { type: "submit", size: "lg", loading: busy, className: "motion-reduce:[&_.animate-spin]:animate-none", children: label })
1900
2142
  ] });
1901
2143
  }
1902
- function DefaultEnding({ title, description, className }) {
2144
+ function DefaultEnding({ title, description, actions, className }) {
1903
2145
  const { presentation } = useFormPresentation();
2146
+ const actionRow = actions ? /* @__PURE__ */ jsx("div", { className: "mt-4 flex flex-wrap justify-center gap-2", children: actions }) : null;
1904
2147
  if (presentation === "page") {
1905
2148
  return /* @__PURE__ */ jsxs("div", { className: cn("flex flex-col items-center gap-4 text-center", className), children: [
1906
2149
  /* @__PURE__ */ jsx(
@@ -1923,12 +2166,14 @@ function DefaultEnding({ title, description, className }) {
1923
2166
  children: title
1924
2167
  }
1925
2168
  ),
1926
- description ? /* @__PURE__ */ jsx("p", { className: "max-w-md text-lg text-muted-foreground text-pretty", children: description }) : null
2169
+ description ? /* @__PURE__ */ jsx("p", { className: "max-w-md text-lg text-muted-foreground text-pretty", children: description }) : null,
2170
+ actionRow
1927
2171
  ] });
1928
2172
  }
1929
2173
  return /* @__PURE__ */ jsxs("div", { className: cn("space-y-2 py-8 text-center", className), children: [
1930
2174
  /* @__PURE__ */ jsx("h2", { tabIndex: -1, "data-gf-focus": "", className: cn("rounded-sm text-2xl font-semibold", focusRing), children: title }),
1931
- description ? /* @__PURE__ */ jsx("p", { className: "text-muted-foreground", children: description }) : null
2175
+ description ? /* @__PURE__ */ jsx("p", { className: "text-muted-foreground", children: description }) : null,
2176
+ actionRow
1932
2177
  ] });
1933
2178
  }
1934
2179
  function focusErrorTarget(event) {
@@ -1980,7 +2225,12 @@ function Form(props) {
1980
2225
  return props.engine ? /* @__PURE__ */ jsx(FormWithEngine, { ...props, engine: props.engine }) : /* @__PURE__ */ jsx(OwnedForm, { ...props });
1981
2226
  }
1982
2227
  function OwnedForm(props) {
1983
- const engine = useForm(props.form, props.hostOptions ? { ...props.options, hostOptions: props.hostOptions } : props.options);
2228
+ const options = props.hostOptions || props.linkParams ? {
2229
+ ...props.options,
2230
+ ...props.hostOptions ? { hostOptions: props.hostOptions } : {},
2231
+ ...props.linkParams ? { linkParams: props.linkParams } : {}
2232
+ } : props.options;
2233
+ const engine = useForm(props.form, options);
1984
2234
  return /* @__PURE__ */ jsx(FormWithEngine, { ...props, engine });
1985
2235
  }
1986
2236
  function FormWithEngine(props) {
@@ -2022,7 +2272,7 @@ function FormBody({
2022
2272
  const layout = layoutProp ?? def.layout?.mode ?? "classic";
2023
2273
  const status = useFormSelector(engine, (s) => s.status);
2024
2274
  const current = useFormSelector(engine, (s) => s.current);
2025
- const pathKey = useFormSelector(engine, (s) => JSON.stringify(s.path));
2275
+ const pathKey = useFormSelector(engine, (s) => JSON.stringify(respondentPath(def, s)));
2026
2276
  const errorsKey = useFormSelector(engine, (s) => JSON.stringify(s.errors));
2027
2277
  const ending = useFormSelector(engine, (s) => s.ending);
2028
2278
  const historyLength = useFormSelector(engine, (s) => s.history.length);
@@ -2152,7 +2402,10 @@ function FormBody({
2152
2402
  sending.current = false;
2153
2403
  }
2154
2404
  };
2155
- const isLastStep = (state) => state.current === state.path[state.path.length - 1];
2405
+ const isLastStep = (state) => {
2406
+ const shown = respondentPath(def, state);
2407
+ return state.current === shown[shown.length - 1];
2408
+ };
2156
2409
  const advance = () => {
2157
2410
  if (waitingForUploads()) return;
2158
2411
  direction.current = "forward";
@@ -2405,6 +2658,75 @@ function FormBody({
2405
2658
  }
2406
2659
  var classicSection = "rounded-xl border bg-card px-5 py-6 text-card-foreground sm:px-8 has-[[aria-invalid=true]]:border-destructive";
2407
2660
 
2408
- export { CLASSIC_PROGRESS_DELAY_MS, FieldChrome, Form, MediaTimeContext, PROGRESS_DELAY_MS, SourceTextContext, UPLOAD_WAIT_MESSAGE, UploadContext, builtInRenderers, defaultRegistry, describedBy, photoAccept, photoFileProblem, photoFileType, themeColors, themeCss, useFormPresentation, useMediaTime, useSourceText, useUpload };
2661
+ // src/signed-upload.ts
2662
+ async function putToUrl(file, upload, contentType, fetcher) {
2663
+ if (!upload.url) throw new Error("The server signed no URL to send the file to; pass `send`.");
2664
+ const res = await fetcher(upload.url, { method: "PUT", headers: { "content-type": contentType }, body: file });
2665
+ if (!res.ok) throw new Error(`The file wasn't stored (${res.status}).`);
2666
+ }
2667
+ function signedUploadHost(options) {
2668
+ const fetcher = (...args) => (options.fetch ?? fetch)(...args);
2669
+ return {
2670
+ async upload(file, field) {
2671
+ const type = photoFileType(file);
2672
+ const claim = typeof options.claim === "function" ? options.claim() : options.claim;
2673
+ const endpoint = typeof options.endpoint === "function" ? options.endpoint(field) : options.endpoint;
2674
+ const request = { fieldId: field.id, name: file.name, type, size: file.size, ...claim };
2675
+ const response = await fetcher(endpoint, {
2676
+ method: "POST",
2677
+ headers: { "content-type": "application/json" },
2678
+ body: JSON.stringify(request)
2679
+ });
2680
+ const body = await response.json().catch(() => ({}));
2681
+ if (!response.ok || !body.ref || !body.upload) {
2682
+ throw new Error(typeof body.error === "string" ? body.error : `The photo wasn't accepted (${response.status}).`);
2683
+ }
2684
+ if (options.send) await options.send(file, body.upload, type);
2685
+ else await putToUrl(file, body.upload, type, fetcher);
2686
+ return body.ref;
2687
+ },
2688
+ async url(ref, field) {
2689
+ if (!options.readUrl) return null;
2690
+ try {
2691
+ return await options.readUrl(ref, field);
2692
+ } catch {
2693
+ return null;
2694
+ }
2695
+ }
2696
+ };
2697
+ }
2698
+
2699
+ // src/clean-url.ts
2700
+ var KEY_PREFIX = "form-query:";
2701
+ var HIDDEN_QUERY_KEEP_MS = 30 * 60 * 1e3;
2702
+ function hideQueryOnLoad(win, isReload, now = Date.now()) {
2703
+ const { pathname, search, hash } = win.location;
2704
+ const key = KEY_PREFIX + pathname;
2705
+ try {
2706
+ if (search && search !== "?") {
2707
+ win.sessionStorage.setItem(key, JSON.stringify({ search, at: now }));
2708
+ const replace = nativeReplaceState(win.history);
2709
+ replace.call(win.history, win.history.state, "", pathname + hash);
2710
+ return "hidden";
2711
+ }
2712
+ const saved = win.sessionStorage.getItem(key);
2713
+ if (!saved) return "none";
2714
+ const { search: kept, at } = JSON.parse(saved);
2715
+ if (isReload && typeof kept === "string" && typeof at === "number" && now - at < HIDDEN_QUERY_KEEP_MS) {
2716
+ win.location.replace(pathname + kept + hash);
2717
+ return "restoring";
2718
+ }
2719
+ win.sessionStorage.removeItem(key);
2720
+ } catch {
2721
+ }
2722
+ return "none";
2723
+ }
2724
+ function nativeReplaceState(history) {
2725
+ const proto = Object.getPrototypeOf(history);
2726
+ return typeof proto?.replaceState === "function" ? proto.replaceState : history.replaceState;
2727
+ }
2728
+ var wasReload = () => typeof performance !== "undefined" && performance.getEntriesByType?.("navigation")[0]?.type === "reload";
2729
+
2730
+ export { CLASSIC_PROGRESS_DELAY_MS, DefaultEnding, FieldChrome, Form, HIDDEN_QUERY_KEEP_MS, MediaTimeContext, PROGRESS_DELAY_MS, SourceTextContext, UPLOAD_WAIT_MESSAGE, UploadContext, builtInRenderers, defaultRegistry, describedBy, hideQueryOnLoad, photoAccept, photoFileProblem, photoFileType, signedUploadHost, themeColors, themeCss, useFormPresentation, useMediaTime, useSourceText, useUpload, wasReload };
2409
2731
  //# sourceMappingURL=index.js.map
2410
2732
  //# sourceMappingURL=index.js.map