@marktiderman/genesis-forms-web 0.2.0 → 0.4.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +52 -2
- package/dist/index.d.ts +129 -2
- package/dist/index.js +814 -64
- package/dist/index.js.map +1 -1
- package/package.json +4 -4
package/dist/index.js
CHANGED
|
@@ -1,19 +1,20 @@
|
|
|
1
1
|
"use client";
|
|
2
|
-
import { createContext,
|
|
2
|
+
import { createContext, useState, useEffect, useRef, useId, useContext, useMemo, useLayoutEffect } from 'react';
|
|
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 {
|
|
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';
|
|
7
8
|
import { Checkbox } from '@marktiderman/genesis-ui/checkbox';
|
|
8
9
|
import { Input } from '@marktiderman/genesis-ui/input';
|
|
9
10
|
import { Label } from '@marktiderman/genesis-ui/label';
|
|
10
11
|
import { RadioGroup, RadioGroupItem } from '@marktiderman/genesis-ui/radio-group';
|
|
11
12
|
import { Textarea } from '@marktiderman/genesis-ui/textarea';
|
|
12
|
-
import {
|
|
13
|
-
import { FormDescription, FormError, FormLabel } from '@marktiderman/genesis-ui/form-field';
|
|
13
|
+
import { FormError, FormDescription, FormLabel } from '@marktiderman/genesis-ui/form-field';
|
|
14
14
|
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
|
+
import { Spinner } from '@marktiderman/genesis-ui/spinner';
|
|
17
18
|
|
|
18
19
|
// src/Form.tsx
|
|
19
20
|
var FormPresentationContext = createContext({ presentation: "embedded", layout: "classic" });
|
|
@@ -120,6 +121,29 @@ function describedBy(props) {
|
|
|
120
121
|
].filter(Boolean);
|
|
121
122
|
return ids.length ? ids.join(" ") : void 0;
|
|
122
123
|
}
|
|
124
|
+
function CalculatedField(props) {
|
|
125
|
+
const { binding, inputId } = props;
|
|
126
|
+
const { presentation, layout } = useFormPresentation();
|
|
127
|
+
const pageFocus = presentation === "page" && layout === "focus";
|
|
128
|
+
const text = formatCalculated(binding.value, binding.field);
|
|
129
|
+
return /* @__PURE__ */ jsx(FieldChrome, { props, children: /* @__PURE__ */ jsx(
|
|
130
|
+
"output",
|
|
131
|
+
{
|
|
132
|
+
id: inputId,
|
|
133
|
+
"aria-labelledby": `${inputId}-label`,
|
|
134
|
+
"aria-describedby": describedBy(props),
|
|
135
|
+
"aria-live": "polite",
|
|
136
|
+
"data-gf-calculated": "",
|
|
137
|
+
className: cn(
|
|
138
|
+
"block rounded-md border border-dashed bg-muted/40 px-3 py-2 font-medium tabular-nums",
|
|
139
|
+
pageFocus ? "text-2xl sm:text-3xl" : "text-base",
|
|
140
|
+
!text && "text-muted-foreground"
|
|
141
|
+
),
|
|
142
|
+
children: text || "\u2014"
|
|
143
|
+
}
|
|
144
|
+
) });
|
|
145
|
+
}
|
|
146
|
+
var calculatedRenderers = [{ type: "calculated", Component: CalculatedField }];
|
|
123
147
|
var MediaTimeContext = createContext(null);
|
|
124
148
|
function useMediaTime() {
|
|
125
149
|
return useContext(MediaTimeContext);
|
|
@@ -749,6 +773,237 @@ var structuredRenderers = [
|
|
|
749
773
|
{ type: "media-notes", Component: MediaNotesField },
|
|
750
774
|
{ type: "text-highlights", Component: TextHighlightsField }
|
|
751
775
|
];
|
|
776
|
+
var UploadContext = createContext(null);
|
|
777
|
+
function useUpload() {
|
|
778
|
+
return useContext(UploadContext);
|
|
779
|
+
}
|
|
780
|
+
var PendingUploadsContext = createContext(null);
|
|
781
|
+
function createPendingUploads() {
|
|
782
|
+
let n = 0;
|
|
783
|
+
return {
|
|
784
|
+
start() {
|
|
785
|
+
n++;
|
|
786
|
+
let done = false;
|
|
787
|
+
return () => {
|
|
788
|
+
if (done) return;
|
|
789
|
+
done = true;
|
|
790
|
+
n--;
|
|
791
|
+
};
|
|
792
|
+
},
|
|
793
|
+
count: () => n
|
|
794
|
+
};
|
|
795
|
+
}
|
|
796
|
+
var UPLOAD_WAIT_MESSAGE = "Wait for the photo to finish uploading.";
|
|
797
|
+
function CameraIcon() {
|
|
798
|
+
return /* @__PURE__ */ jsxs("svg", { viewBox: "0 0 24 24", fill: "none", stroke: "currentColor", strokeWidth: "2", strokeLinecap: "round", strokeLinejoin: "round", "aria-hidden": "true", children: [
|
|
799
|
+
/* @__PURE__ */ jsx("path", { d: "M14.5 4h-5L7 7H4a2 2 0 0 0-2 2v9a2 2 0 0 0 2 2h16a2 2 0 0 0 2-2V9a2 2 0 0 0-2-2h-3l-2.5-3z" }),
|
|
800
|
+
/* @__PURE__ */ jsx("circle", { cx: "12", cy: "13", r: "3" })
|
|
801
|
+
] });
|
|
802
|
+
}
|
|
803
|
+
function ImageIcon({ className }) {
|
|
804
|
+
return /* @__PURE__ */ jsxs("svg", { viewBox: "0 0 24 24", fill: "none", stroke: "currentColor", strokeWidth: "2", strokeLinecap: "round", strokeLinejoin: "round", "aria-hidden": "true", className, children: [
|
|
805
|
+
/* @__PURE__ */ jsx("rect", { x: "3", y: "3", width: "18", height: "18", rx: "2" }),
|
|
806
|
+
/* @__PURE__ */ jsx("circle", { cx: "9", cy: "9", r: "2" }),
|
|
807
|
+
/* @__PURE__ */ jsx("path", { d: "m21 15-3.1-3.1a2 2 0 0 0-2.8 0L6 21" })
|
|
808
|
+
] });
|
|
809
|
+
}
|
|
810
|
+
function XIcon2() {
|
|
811
|
+
return /* @__PURE__ */ jsx("svg", { viewBox: "0 0 24 24", fill: "none", stroke: "currentColor", strokeWidth: "2", strokeLinecap: "round", "aria-hidden": "true", children: /* @__PURE__ */ jsx("path", { d: "M18 6 6 18M6 6l12 12" }) });
|
|
812
|
+
}
|
|
813
|
+
var HEIC_NAME = /\.hei[cf]$/i;
|
|
814
|
+
function photoFileType(file) {
|
|
815
|
+
const type = file.type.toLowerCase();
|
|
816
|
+
if (type) return type;
|
|
817
|
+
return HEIC_NAME.test(file.name) ? file.name.toLowerCase().endsWith(".heif") ? "image/heif" : "image/heic" : "";
|
|
818
|
+
}
|
|
819
|
+
function photoFileProblem(field, file) {
|
|
820
|
+
const limits = photoLimits(field);
|
|
821
|
+
if (!limits.mimeTypes.includes(photoFileType(file))) return photoTypeMessage(field);
|
|
822
|
+
if (file.size <= 0 || file.size > limits.maxBytes) return photoSizeMessage(field);
|
|
823
|
+
return null;
|
|
824
|
+
}
|
|
825
|
+
function photoAccept(field) {
|
|
826
|
+
const limits = photoLimits(field);
|
|
827
|
+
return [...limits.mimeTypes, ...limits.formats.includes("heic") ? [".heic", ".heif"] : []].join(",");
|
|
828
|
+
}
|
|
829
|
+
function Tile({ name, preview, busy }) {
|
|
830
|
+
const [failed, setFailed] = useState(false);
|
|
831
|
+
return /* @__PURE__ */ jsxs("div", { className: "relative size-24 overflow-hidden rounded-md border bg-muted sm:size-28", children: [
|
|
832
|
+
preview && !failed ? /* @__PURE__ */ jsx("img", { src: preview, alt: name, className: cn("size-full object-cover", busy && "opacity-60"), onError: () => setFailed(true) }) : /* @__PURE__ */ jsxs("div", { className: "flex size-full flex-col items-center justify-center gap-1 p-2 text-center text-xs text-muted-foreground", children: [
|
|
833
|
+
/* @__PURE__ */ jsx(ImageIcon, { className: "size-6" }),
|
|
834
|
+
/* @__PURE__ */ jsx("span", { className: "line-clamp-2 break-all", children: name })
|
|
835
|
+
] }),
|
|
836
|
+
busy ? /* @__PURE__ */ jsx("div", { className: "absolute inset-0 flex items-center justify-center", children: /* @__PURE__ */ jsx(Spinner, { size: "sm", "aria-hidden": "true" }) }) : null
|
|
837
|
+
] });
|
|
838
|
+
}
|
|
839
|
+
var nextKey = 0;
|
|
840
|
+
function PhotoField(props) {
|
|
841
|
+
const { binding, inputId, engine } = props;
|
|
842
|
+
const { field } = binding;
|
|
843
|
+
const host = useUpload();
|
|
844
|
+
const pending = useContext(PendingUploadsContext);
|
|
845
|
+
const locked = useFormSelector(engine, (s) => s.status === "completed");
|
|
846
|
+
const limits = photoLimits(field);
|
|
847
|
+
const refs = photoRefs(binding.value);
|
|
848
|
+
const [uploading, setUploading] = useState([]);
|
|
849
|
+
const [problem, setProblem] = useState(null);
|
|
850
|
+
const [coarse, setCoarse] = useState(false);
|
|
851
|
+
const previews = useRef(/* @__PURE__ */ new Map());
|
|
852
|
+
const releases = useRef(/* @__PURE__ */ new Set());
|
|
853
|
+
const mounted = useRef(true);
|
|
854
|
+
const chooseRef = useRef(null);
|
|
855
|
+
const takeRef = useRef(null);
|
|
856
|
+
useEffect(() => {
|
|
857
|
+
setCoarse(typeof window !== "undefined" && !!window.matchMedia?.("(pointer: coarse)").matches);
|
|
858
|
+
}, []);
|
|
859
|
+
useEffect(() => {
|
|
860
|
+
const urls = previews.current;
|
|
861
|
+
const pendingReleases = releases.current;
|
|
862
|
+
mounted.current = true;
|
|
863
|
+
return () => {
|
|
864
|
+
mounted.current = false;
|
|
865
|
+
for (const url of urls.values()) URL.revokeObjectURL(url);
|
|
866
|
+
urls.clear();
|
|
867
|
+
for (const release of pendingReleases) release();
|
|
868
|
+
pendingReleases.clear();
|
|
869
|
+
};
|
|
870
|
+
}, []);
|
|
871
|
+
const currentRefs = () => photoRefs(engine.getState().values[field.id]);
|
|
872
|
+
const full = refs.length + uploading.length >= limits.maxFiles;
|
|
873
|
+
const disabled = !host || locked;
|
|
874
|
+
const add = (files) => {
|
|
875
|
+
if (!host) return;
|
|
876
|
+
setProblem(null);
|
|
877
|
+
let room = limits.maxFiles - currentRefs().length - uploading.length;
|
|
878
|
+
for (const file of files) {
|
|
879
|
+
if (room <= 0) {
|
|
880
|
+
setProblem(photoCountMessage(field));
|
|
881
|
+
break;
|
|
882
|
+
}
|
|
883
|
+
const why = photoFileProblem(field, file);
|
|
884
|
+
if (why) {
|
|
885
|
+
setProblem(why);
|
|
886
|
+
continue;
|
|
887
|
+
}
|
|
888
|
+
room--;
|
|
889
|
+
const key = `u${nextKey++}`;
|
|
890
|
+
const preview = typeof URL.createObjectURL === "function" ? URL.createObjectURL(file) : null;
|
|
891
|
+
setUploading((list) => [...list, { key, name: file.name, preview }]);
|
|
892
|
+
const release = pending?.start() ?? (() => {
|
|
893
|
+
});
|
|
894
|
+
releases.current.add(release);
|
|
895
|
+
const finish = () => {
|
|
896
|
+
release();
|
|
897
|
+
releases.current.delete(release);
|
|
898
|
+
setUploading((list) => list.filter((u) => u.key !== key));
|
|
899
|
+
};
|
|
900
|
+
Promise.resolve().then(() => host.upload(file, field)).then((ref) => {
|
|
901
|
+
if (!isPhotoRef(ref)) throw new Error("The upload returned no photo reference.");
|
|
902
|
+
if (preview) {
|
|
903
|
+
const old = previews.current.get(ref.id);
|
|
904
|
+
if (old && old !== preview) URL.revokeObjectURL(old);
|
|
905
|
+
if (mounted.current) previews.current.set(ref.id, preview);
|
|
906
|
+
else URL.revokeObjectURL(preview);
|
|
907
|
+
}
|
|
908
|
+
const next = [...currentRefs().filter((r) => r.id !== ref.id), { id: ref.id, name: ref.name, type: ref.type, size: ref.size }];
|
|
909
|
+
binding.setValue(next);
|
|
910
|
+
finish();
|
|
911
|
+
}).catch(() => {
|
|
912
|
+
if (preview) URL.revokeObjectURL(preview);
|
|
913
|
+
setProblem(`\u201C${file.name}\u201D didn't upload. Try again.`);
|
|
914
|
+
finish();
|
|
915
|
+
});
|
|
916
|
+
}
|
|
917
|
+
};
|
|
918
|
+
const onPick = (e) => {
|
|
919
|
+
const files = Array.from(e.target.files ?? []);
|
|
920
|
+
e.target.value = "";
|
|
921
|
+
add(files);
|
|
922
|
+
};
|
|
923
|
+
const remove = (ref) => {
|
|
924
|
+
const url = previews.current.get(ref.id);
|
|
925
|
+
if (url) URL.revokeObjectURL(url);
|
|
926
|
+
previews.current.delete(ref.id);
|
|
927
|
+
const next = currentRefs().filter((r) => r.id !== ref.id);
|
|
928
|
+
binding.setValue(next.length ? next : void 0);
|
|
929
|
+
setProblem(null);
|
|
930
|
+
};
|
|
931
|
+
const accept = photoAccept(field);
|
|
932
|
+
const errorId = `${inputId}-upload-error`;
|
|
933
|
+
const hasAny = refs.length > 0 || uploading.length > 0;
|
|
934
|
+
return /* @__PURE__ */ jsx(FieldChrome, { props, children: /* @__PURE__ */ jsxs("div", { className: "space-y-3", children: [
|
|
935
|
+
hasAny ? /* @__PURE__ */ jsxs("ul", { className: "flex flex-wrap gap-3", "aria-label": "Photos", children: [
|
|
936
|
+
refs.map((ref) => /* @__PURE__ */ jsxs("li", { className: "relative", children: [
|
|
937
|
+
/* @__PURE__ */ jsx(Tile, { name: ref.name, preview: previews.current.get(ref.id) ?? null }),
|
|
938
|
+
locked ? null : /* @__PURE__ */ jsx(
|
|
939
|
+
Button,
|
|
940
|
+
{
|
|
941
|
+
type: "button",
|
|
942
|
+
variant: "secondary",
|
|
943
|
+
size: "icon",
|
|
944
|
+
"aria-label": `Remove ${ref.name}`,
|
|
945
|
+
onClick: () => remove(ref),
|
|
946
|
+
className: "absolute right-1 top-1 size-7 rounded-full shadow-sm",
|
|
947
|
+
children: /* @__PURE__ */ jsx(XIcon2, {})
|
|
948
|
+
}
|
|
949
|
+
)
|
|
950
|
+
] }, ref.id)),
|
|
951
|
+
uploading.map((u) => /* @__PURE__ */ jsx("li", { "aria-label": `Uploading ${u.name}`, children: /* @__PURE__ */ jsx(Tile, { name: u.name, preview: u.preview, busy: true }) }, u.key))
|
|
952
|
+
] }) : null,
|
|
953
|
+
/* @__PURE__ */ jsxs("div", { className: "flex flex-wrap gap-2", children: [
|
|
954
|
+
/* @__PURE__ */ jsxs(
|
|
955
|
+
Button,
|
|
956
|
+
{
|
|
957
|
+
type: "button",
|
|
958
|
+
variant: "outline",
|
|
959
|
+
disabled: disabled || full,
|
|
960
|
+
"aria-invalid": binding.error ? true : void 0,
|
|
961
|
+
onClick: () => chooseRef.current?.click(),
|
|
962
|
+
children: [
|
|
963
|
+
/* @__PURE__ */ jsx(ImageIcon, {}),
|
|
964
|
+
" ",
|
|
965
|
+
refs.length ? "Add another photo" : "Choose a photo"
|
|
966
|
+
]
|
|
967
|
+
}
|
|
968
|
+
),
|
|
969
|
+
coarse ? /* @__PURE__ */ jsxs(Button, { type: "button", variant: "outline", disabled: disabled || full, onClick: () => takeRef.current?.click(), children: [
|
|
970
|
+
/* @__PURE__ */ jsx(CameraIcon, {}),
|
|
971
|
+
" Take a photo"
|
|
972
|
+
] }) : null
|
|
973
|
+
] }),
|
|
974
|
+
/* @__PURE__ */ jsx(
|
|
975
|
+
Input,
|
|
976
|
+
{
|
|
977
|
+
ref: chooseRef,
|
|
978
|
+
type: "file",
|
|
979
|
+
accept,
|
|
980
|
+
multiple: limits.maxFiles > 1,
|
|
981
|
+
className: "hidden",
|
|
982
|
+
tabIndex: -1,
|
|
983
|
+
"aria-hidden": "true",
|
|
984
|
+
disabled,
|
|
985
|
+
onChange: onPick
|
|
986
|
+
}
|
|
987
|
+
),
|
|
988
|
+
/* @__PURE__ */ jsx(
|
|
989
|
+
Input,
|
|
990
|
+
{
|
|
991
|
+
ref: takeRef,
|
|
992
|
+
type: "file",
|
|
993
|
+
accept,
|
|
994
|
+
capture: "environment",
|
|
995
|
+
className: "hidden",
|
|
996
|
+
tabIndex: -1,
|
|
997
|
+
"aria-hidden": "true",
|
|
998
|
+
disabled,
|
|
999
|
+
onChange: onPick
|
|
1000
|
+
}
|
|
1001
|
+
),
|
|
1002
|
+
!host ? /* @__PURE__ */ jsx("p", { className: "text-sm text-muted-foreground", children: "Photos can't be added here." }) : /* @__PURE__ */ jsx("p", { className: "text-sm text-muted-foreground", children: `Up to ${limits.maxFiles} ${limits.maxFiles === 1 ? "photo" : "photos"}, ${limits.maxSizeMb} MB each.` }),
|
|
1003
|
+
problem ? /* @__PURE__ */ jsx(FormError, { id: errorId, role: "alert", children: problem }) : null
|
|
1004
|
+
] }) });
|
|
1005
|
+
}
|
|
1006
|
+
var photoRenderers = [{ type: "photo", Component: PhotoField }];
|
|
752
1007
|
function groupAria(props) {
|
|
753
1008
|
return {
|
|
754
1009
|
"aria-labelledby": `${props.inputId}-label`,
|
|
@@ -894,32 +1149,88 @@ function scaleValues(type, max) {
|
|
|
894
1149
|
if (type === "nps") return Array.from({ length: 11 }, (_, i) => i);
|
|
895
1150
|
return Array.from({ length: max }, (_, i) => i + 1);
|
|
896
1151
|
}
|
|
1152
|
+
function endLabels(props) {
|
|
1153
|
+
const own = scaleLabels(props.binding.field);
|
|
1154
|
+
if (props.binding.field.type !== "nps") return own;
|
|
1155
|
+
return { low: own.low ?? "Not likely", high: own.high ?? "Very likely" };
|
|
1156
|
+
}
|
|
1157
|
+
function EndLabels({ inputId, low, high }) {
|
|
1158
|
+
if (!low && !high) return null;
|
|
1159
|
+
return /* @__PURE__ */ jsxs("div", { className: "flex justify-between gap-4 text-sm text-muted-foreground", "data-gf-scale-labels": "", children: [
|
|
1160
|
+
/* @__PURE__ */ jsx("span", { id: `${inputId}-low`, children: low ?? "" }),
|
|
1161
|
+
/* @__PURE__ */ jsx("span", { id: `${inputId}-high`, className: "text-right", children: high ?? "" })
|
|
1162
|
+
] });
|
|
1163
|
+
}
|
|
897
1164
|
function ScaleField(props) {
|
|
898
1165
|
const { binding, inputId } = props;
|
|
899
1166
|
const max = typeof binding.field.props?.max === "number" ? binding.field.props.max : 5;
|
|
900
1167
|
const values = scaleValues(binding.field.type, max);
|
|
901
|
-
const
|
|
902
|
-
const
|
|
903
|
-
|
|
904
|
-
|
|
905
|
-
|
|
906
|
-
|
|
1168
|
+
const { low, high } = endLabels(props);
|
|
1169
|
+
const stars = binding.field.type === "rating" && ratingDisplay(binding.field) === "stars";
|
|
1170
|
+
const describe = (i) => low && i === 0 ? `${inputId}-low` : high && i === values.length - 1 ? `${inputId}-high` : void 0;
|
|
1171
|
+
const value = typeof binding.value === "number" ? String(binding.value) : "";
|
|
1172
|
+
const onValueChange = (v) => binding.setValue(Number(v));
|
|
1173
|
+
if (stars) {
|
|
1174
|
+
return /* @__PURE__ */ jsxs(FieldChrome, { props, children: [
|
|
1175
|
+
/* @__PURE__ */ jsx(StarRadioGroup, { props, values, value, onValueChange, describe }),
|
|
1176
|
+
/* @__PURE__ */ jsx(EndLabels, { inputId, low, high })
|
|
1177
|
+
] });
|
|
1178
|
+
}
|
|
1179
|
+
const options = values.map((n, i) => ({ value: String(n), label: String(n), describedBy: describe(i) }));
|
|
907
1180
|
return /* @__PURE__ */ jsxs(FieldChrome, { props, children: [
|
|
908
|
-
/* @__PURE__ */ jsx(
|
|
909
|
-
|
|
910
|
-
{
|
|
911
|
-
props,
|
|
912
|
-
options,
|
|
913
|
-
value: typeof binding.value === "number" ? String(binding.value) : "",
|
|
914
|
-
onValueChange: (v) => binding.setValue(Number(v))
|
|
915
|
-
}
|
|
916
|
-
),
|
|
917
|
-
nps ? /* @__PURE__ */ jsxs("div", { className: "flex justify-between text-sm text-muted-foreground", children: [
|
|
918
|
-
/* @__PURE__ */ jsx("span", { id: `${inputId}-low`, children: "Not likely" }),
|
|
919
|
-
/* @__PURE__ */ jsx("span", { id: `${inputId}-high`, children: "Very likely" })
|
|
920
|
-
] }) : null
|
|
1181
|
+
/* @__PURE__ */ jsx(ButtonRadioGroup, { props, options, value, onValueChange }),
|
|
1182
|
+
/* @__PURE__ */ jsx(EndLabels, { inputId, low, high })
|
|
921
1183
|
] });
|
|
922
1184
|
}
|
|
1185
|
+
function StarIcon({ filled }) {
|
|
1186
|
+
return /* @__PURE__ */ jsx(
|
|
1187
|
+
"svg",
|
|
1188
|
+
{
|
|
1189
|
+
viewBox: "0 0 24 24",
|
|
1190
|
+
"aria-hidden": "true",
|
|
1191
|
+
className: cn("size-8 transition-colors motion-reduce:transition-none", filled ? "fill-primary text-primary" : "fill-transparent text-muted-foreground"),
|
|
1192
|
+
stroke: "currentColor",
|
|
1193
|
+
strokeWidth: "1.75",
|
|
1194
|
+
strokeLinejoin: "round",
|
|
1195
|
+
children: /* @__PURE__ */ jsx("path", { d: "M12 2.5l2.9 6.1 6.6.8-4.9 4.6 1.3 6.6L12 17.3l-5.9 3.3 1.3-6.6-4.9-4.6 6.6-.8z" })
|
|
1196
|
+
}
|
|
1197
|
+
);
|
|
1198
|
+
}
|
|
1199
|
+
function StarRadioGroup({
|
|
1200
|
+
props,
|
|
1201
|
+
values,
|
|
1202
|
+
value,
|
|
1203
|
+
onValueChange,
|
|
1204
|
+
describe
|
|
1205
|
+
}) {
|
|
1206
|
+
const pick = useDeliberatePick(props.onAdvance);
|
|
1207
|
+
const chosen = value === "" ? 0 : Number(value);
|
|
1208
|
+
const max = values[values.length - 1] ?? 5;
|
|
1209
|
+
return /* @__PURE__ */ jsx(
|
|
1210
|
+
RadioGroup,
|
|
1211
|
+
{
|
|
1212
|
+
...groupAria(props),
|
|
1213
|
+
...pick.rootProps,
|
|
1214
|
+
value,
|
|
1215
|
+
onValueChange,
|
|
1216
|
+
className: "flex flex-wrap justify-start gap-1",
|
|
1217
|
+
"data-gf-stars": "",
|
|
1218
|
+
children: values.map((n, i) => /* @__PURE__ */ jsxs("div", { className: "relative", children: [
|
|
1219
|
+
/* @__PURE__ */ jsx(
|
|
1220
|
+
RadioGroupItem,
|
|
1221
|
+
{
|
|
1222
|
+
value: String(n),
|
|
1223
|
+
"aria-label": `${n} of ${max}`,
|
|
1224
|
+
"aria-describedby": describe(i),
|
|
1225
|
+
onClick: pick.onItemClick,
|
|
1226
|
+
className: "peer size-11 aspect-auto rounded-md border-0 bg-transparent shadow-none hover:bg-accent data-[state=checked]:bg-transparent [&_svg]:hidden"
|
|
1227
|
+
}
|
|
1228
|
+
),
|
|
1229
|
+
/* @__PURE__ */ jsx("span", { "aria-hidden": "true", className: "pointer-events-none absolute inset-0 flex items-center justify-center peer-disabled:opacity-50", children: /* @__PURE__ */ jsx(StarIcon, { filled: n <= chosen }) })
|
|
1230
|
+
] }, n))
|
|
1231
|
+
}
|
|
1232
|
+
);
|
|
1233
|
+
}
|
|
923
1234
|
function YesNoField(props) {
|
|
924
1235
|
const { binding } = props;
|
|
925
1236
|
const current = binding.value === true ? "yes" : binding.value === false ? "no" : "";
|
|
@@ -956,6 +1267,7 @@ function useChoiceLook(inputId, count) {
|
|
|
956
1267
|
control: pageFocus ? pageChoiceKey : void 0,
|
|
957
1268
|
onKeyDown: pageFocus ? (e) => {
|
|
958
1269
|
if (e.altKey || e.ctrlKey || e.metaKey || e.key.length !== 1) return;
|
|
1270
|
+
if (e.target.tagName === "INPUT") return;
|
|
959
1271
|
const index = KEYS.indexOf(e.key.toUpperCase());
|
|
960
1272
|
if (index < 0 || index >= count) return;
|
|
961
1273
|
const option = document.getElementById(`${inputId}-${index}`);
|
|
@@ -994,44 +1306,118 @@ function ChoiceContent({ look, index, label }) {
|
|
|
994
1306
|
)
|
|
995
1307
|
] });
|
|
996
1308
|
}
|
|
1309
|
+
var OTHER_VALUE = "\0other";
|
|
1310
|
+
function OtherText({
|
|
1311
|
+
id,
|
|
1312
|
+
label,
|
|
1313
|
+
value,
|
|
1314
|
+
onChange,
|
|
1315
|
+
invalid: invalid2
|
|
1316
|
+
}) {
|
|
1317
|
+
const line = useLineClass();
|
|
1318
|
+
return /* @__PURE__ */ jsx(
|
|
1319
|
+
Input,
|
|
1320
|
+
{
|
|
1321
|
+
id,
|
|
1322
|
+
value,
|
|
1323
|
+
maxLength: OTHER_MAX_LENGTH,
|
|
1324
|
+
"aria-label": `${label}: your answer`,
|
|
1325
|
+
"aria-invalid": invalid2 || void 0,
|
|
1326
|
+
placeholder: "Type your answer",
|
|
1327
|
+
onChange: (e) => onChange(e.target.value),
|
|
1328
|
+
className: cn("mt-2", line)
|
|
1329
|
+
}
|
|
1330
|
+
);
|
|
1331
|
+
}
|
|
1332
|
+
var focusSoon = (id) => requestAnimationFrame(() => document.getElementById(id)?.focus());
|
|
1333
|
+
function useOtherText(typed) {
|
|
1334
|
+
const [on, setOn] = useState(typed !== "");
|
|
1335
|
+
const [text, setText] = useState(typed);
|
|
1336
|
+
return { on, setOn, text, setText };
|
|
1337
|
+
}
|
|
997
1338
|
function SingleChoiceField(props) {
|
|
998
1339
|
const { binding, inputId, onAdvance } = props;
|
|
999
1340
|
const pick = useDeliberatePick(onAdvance);
|
|
1000
1341
|
const options = choiceOptions(binding.field);
|
|
1001
|
-
const
|
|
1002
|
-
|
|
1003
|
-
|
|
1004
|
-
|
|
1005
|
-
|
|
1006
|
-
|
|
1007
|
-
|
|
1008
|
-
|
|
1009
|
-
|
|
1010
|
-
|
|
1011
|
-
|
|
1012
|
-
/* @__PURE__ */ jsx(
|
|
1013
|
-
RadioGroupItem,
|
|
1014
|
-
{
|
|
1015
|
-
id: `${inputId}-${i}`,
|
|
1016
|
-
value: o.value,
|
|
1017
|
-
onClick: pick.onItemClick,
|
|
1018
|
-
className: look.control
|
|
1019
|
-
}
|
|
1020
|
-
),
|
|
1021
|
-
/* @__PURE__ */ jsx(ChoiceContent, { look, index: i, label: o.label ?? o.value })
|
|
1022
|
-
] }, o.value))
|
|
1342
|
+
const other = otherOption(binding.field);
|
|
1343
|
+
const current = typeof binding.value === "string" ? binding.value : "";
|
|
1344
|
+
const typed = other && current && !options.some((o) => o.value === current) ? current : "";
|
|
1345
|
+
const otherText = useOtherText(typed);
|
|
1346
|
+
const look = useChoiceLook(inputId, options.length + (other ? 1 : 0));
|
|
1347
|
+
const textId = `${inputId}-other-text`;
|
|
1348
|
+
const choose = (v) => {
|
|
1349
|
+
if (v !== OTHER_VALUE) {
|
|
1350
|
+
otherText.setOn(false);
|
|
1351
|
+
binding.setValue(v);
|
|
1352
|
+
return;
|
|
1023
1353
|
}
|
|
1024
|
-
|
|
1354
|
+
otherText.setOn(true);
|
|
1355
|
+
binding.setValue(otherText.text.trim() ? otherText.text : void 0);
|
|
1356
|
+
focusSoon(textId);
|
|
1357
|
+
};
|
|
1358
|
+
return /* @__PURE__ */ jsxs(FieldChrome, { props, children: [
|
|
1359
|
+
/* @__PURE__ */ jsxs(
|
|
1360
|
+
RadioGroup,
|
|
1361
|
+
{
|
|
1362
|
+
...groupAria(props),
|
|
1363
|
+
...pick.rootProps,
|
|
1364
|
+
onKeyDown: look.onKeyDown,
|
|
1365
|
+
className: look.pageFocus ? "gap-2.5" : void 0,
|
|
1366
|
+
value: otherText.on ? OTHER_VALUE : current,
|
|
1367
|
+
onValueChange: choose,
|
|
1368
|
+
children: [
|
|
1369
|
+
options.map((o, i) => /* @__PURE__ */ jsxs(Label, { htmlFor: `${inputId}-${i}`, className: look.row, children: [
|
|
1370
|
+
/* @__PURE__ */ jsx(
|
|
1371
|
+
RadioGroupItem,
|
|
1372
|
+
{
|
|
1373
|
+
id: `${inputId}-${i}`,
|
|
1374
|
+
value: o.value,
|
|
1375
|
+
onClick: pick.onItemClick,
|
|
1376
|
+
className: look.control
|
|
1377
|
+
}
|
|
1378
|
+
),
|
|
1379
|
+
/* @__PURE__ */ jsx(ChoiceContent, { look, index: i, label: o.label ?? o.value })
|
|
1380
|
+
] }, o.value)),
|
|
1381
|
+
other ? (
|
|
1382
|
+
// No auto-advance: the respondent still has to type.
|
|
1383
|
+
/* @__PURE__ */ jsxs(Label, { htmlFor: `${inputId}-${options.length}`, className: look.row, children: [
|
|
1384
|
+
/* @__PURE__ */ jsx(RadioGroupItem, { id: `${inputId}-${options.length}`, value: OTHER_VALUE, className: look.control }),
|
|
1385
|
+
/* @__PURE__ */ jsx(ChoiceContent, { look, index: options.length, label: other.label })
|
|
1386
|
+
] })
|
|
1387
|
+
) : null
|
|
1388
|
+
]
|
|
1389
|
+
}
|
|
1390
|
+
),
|
|
1391
|
+
other && otherText.on ? /* @__PURE__ */ jsx(
|
|
1392
|
+
OtherText,
|
|
1393
|
+
{
|
|
1394
|
+
id: textId,
|
|
1395
|
+
label: other.label,
|
|
1396
|
+
value: otherText.text,
|
|
1397
|
+
invalid: !!binding.error,
|
|
1398
|
+
onChange: (text) => {
|
|
1399
|
+
otherText.setText(text);
|
|
1400
|
+
binding.setValue(text.trim() ? text : void 0);
|
|
1401
|
+
}
|
|
1402
|
+
}
|
|
1403
|
+
) : null
|
|
1404
|
+
] });
|
|
1025
1405
|
}
|
|
1026
1406
|
function MultiChoiceField(props) {
|
|
1027
1407
|
const { binding, inputId } = props;
|
|
1028
1408
|
const selected = Array.isArray(binding.value) ? binding.value : [];
|
|
1029
1409
|
const options = choiceOptions(binding.field);
|
|
1030
|
-
const
|
|
1031
|
-
const
|
|
1032
|
-
|
|
1410
|
+
const other = otherOption(binding.field);
|
|
1411
|
+
const known = new Set(options.map((o) => o.value));
|
|
1412
|
+
const chosen = selected.filter((v) => known.has(v));
|
|
1413
|
+
const otherText = useOtherText(other ? selected.find((v) => !known.has(v)) ?? "" : "");
|
|
1414
|
+
const look = useChoiceLook(inputId, options.length + (other ? 1 : 0));
|
|
1415
|
+
const textId = `${inputId}-other-text`;
|
|
1416
|
+
const commit = (picked, otherOn, text) => {
|
|
1417
|
+
const next = otherOn && text.trim() ? [...picked, text] : picked;
|
|
1033
1418
|
binding.setValue(next.length ? next : void 0);
|
|
1034
1419
|
};
|
|
1420
|
+
const toggle = (value, on) => commit(on ? [...chosen, value] : chosen.filter((v) => v !== value), otherText.on, otherText.text);
|
|
1035
1421
|
return /* @__PURE__ */ jsx(FieldChrome, { props, children: /* @__PURE__ */ jsxs("div", { className: cn("grid gap-2", look.pageFocus && "gap-2.5"), onKeyDown: look.onKeyDown, children: [
|
|
1036
1422
|
look.pageFocus ? /* @__PURE__ */ jsx("p", { className: "-mt-2 mb-1 text-sm text-muted-foreground", children: "Choose as many as you like" }) : null,
|
|
1037
1423
|
options.map((o, i) => /* @__PURE__ */ jsxs(Label, { htmlFor: `${inputId}-${i}`, className: look.row, children: [
|
|
@@ -1046,7 +1432,174 @@ function MultiChoiceField(props) {
|
|
|
1046
1432
|
}
|
|
1047
1433
|
),
|
|
1048
1434
|
/* @__PURE__ */ jsx(ChoiceContent, { look, index: i, label: o.label ?? o.value })
|
|
1049
|
-
] }, o.value))
|
|
1435
|
+
] }, o.value)),
|
|
1436
|
+
other ? /* @__PURE__ */ jsxs(Label, { htmlFor: `${inputId}-${options.length}`, className: look.row, children: [
|
|
1437
|
+
/* @__PURE__ */ jsx(
|
|
1438
|
+
Checkbox,
|
|
1439
|
+
{
|
|
1440
|
+
id: `${inputId}-${options.length}`,
|
|
1441
|
+
checked: otherText.on,
|
|
1442
|
+
"aria-invalid": binding.error ? true : void 0,
|
|
1443
|
+
onCheckedChange: (c) => {
|
|
1444
|
+
otherText.setOn(c === true);
|
|
1445
|
+
commit(chosen, c === true, otherText.text);
|
|
1446
|
+
if (c === true) focusSoon(textId);
|
|
1447
|
+
},
|
|
1448
|
+
className: look.control
|
|
1449
|
+
}
|
|
1450
|
+
),
|
|
1451
|
+
/* @__PURE__ */ jsx(ChoiceContent, { look, index: options.length, label: other.label })
|
|
1452
|
+
] }) : null,
|
|
1453
|
+
other && otherText.on ? /* @__PURE__ */ jsx(
|
|
1454
|
+
OtherText,
|
|
1455
|
+
{
|
|
1456
|
+
id: textId,
|
|
1457
|
+
label: other.label,
|
|
1458
|
+
value: otherText.text,
|
|
1459
|
+
invalid: !!binding.error,
|
|
1460
|
+
onChange: (text) => {
|
|
1461
|
+
otherText.setText(text);
|
|
1462
|
+
commit(chosen, true, text);
|
|
1463
|
+
}
|
|
1464
|
+
}
|
|
1465
|
+
) : null
|
|
1466
|
+
] }) });
|
|
1467
|
+
}
|
|
1468
|
+
function useLineClass() {
|
|
1469
|
+
const { presentation, layout } = useFormPresentation();
|
|
1470
|
+
if (presentation === "page" && layout === "focus") {
|
|
1471
|
+
return "h-auto rounded-none border-0 border-b-2 border-input bg-transparent px-0 py-2 text-xl shadow-none md:text-2xl sm:text-2xl focus-visible:border-primary focus-visible:ring-0 focus-visible:ring-offset-0 aria-invalid:border-destructive";
|
|
1472
|
+
}
|
|
1473
|
+
return presentation === "page" ? "h-11 md:text-base" : void 0;
|
|
1474
|
+
}
|
|
1475
|
+
function controlAria(props) {
|
|
1476
|
+
return {
|
|
1477
|
+
id: props.inputId,
|
|
1478
|
+
"data-gf-focus": "",
|
|
1479
|
+
"aria-invalid": props.binding.error ? true : void 0,
|
|
1480
|
+
"aria-required": props.binding.field.required || void 0,
|
|
1481
|
+
"aria-describedby": describedBy(props)
|
|
1482
|
+
};
|
|
1483
|
+
}
|
|
1484
|
+
function DropdownField(props) {
|
|
1485
|
+
const { binding } = props;
|
|
1486
|
+
const options = choiceOptions(binding.field);
|
|
1487
|
+
const other = otherOption(binding.field);
|
|
1488
|
+
const line = useLineClass();
|
|
1489
|
+
const current = typeof binding.value === "string" ? binding.value : "";
|
|
1490
|
+
const typed = other && current && !options.some((o) => o.value === current) ? current : "";
|
|
1491
|
+
const otherText = useOtherText(typed);
|
|
1492
|
+
const value = otherText.on ? OTHER_VALUE : current;
|
|
1493
|
+
const textId = `${props.inputId}-other-text`;
|
|
1494
|
+
return /* @__PURE__ */ jsxs(FieldChrome, { props, children: [
|
|
1495
|
+
/* @__PURE__ */ jsxs(
|
|
1496
|
+
"select",
|
|
1497
|
+
{
|
|
1498
|
+
...controlAria(props),
|
|
1499
|
+
value,
|
|
1500
|
+
onChange: (e) => {
|
|
1501
|
+
const v = e.target.value;
|
|
1502
|
+
otherText.setOn(v === OTHER_VALUE);
|
|
1503
|
+
if (v === OTHER_VALUE) {
|
|
1504
|
+
binding.setValue(otherText.text.trim() ? otherText.text : void 0);
|
|
1505
|
+
focusSoon(textId);
|
|
1506
|
+
} else binding.setValue(v === "" ? void 0 : v);
|
|
1507
|
+
},
|
|
1508
|
+
className: cn(
|
|
1509
|
+
"flex h-10 w-full min-w-0 rounded-md border border-input bg-background px-3 py-2 text-base ring-offset-background focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-ring focus-visible:ring-offset-2 disabled:cursor-not-allowed disabled:opacity-50 md:text-sm aria-invalid:border-destructive",
|
|
1510
|
+
value === "" && "text-muted-foreground",
|
|
1511
|
+
line
|
|
1512
|
+
),
|
|
1513
|
+
children: [
|
|
1514
|
+
/* @__PURE__ */ jsx("option", { value: "", children: binding.field.placeholder ?? "Choose\u2026" }),
|
|
1515
|
+
options.map((o) => /* @__PURE__ */ jsx("option", { value: o.value, children: o.label ?? o.value }, o.value)),
|
|
1516
|
+
other ? /* @__PURE__ */ jsx("option", { value: OTHER_VALUE, children: other.label }) : null
|
|
1517
|
+
]
|
|
1518
|
+
}
|
|
1519
|
+
),
|
|
1520
|
+
other && otherText.on ? /* @__PURE__ */ jsx(
|
|
1521
|
+
OtherText,
|
|
1522
|
+
{
|
|
1523
|
+
id: textId,
|
|
1524
|
+
label: other.label,
|
|
1525
|
+
value: otherText.text,
|
|
1526
|
+
invalid: !!binding.error,
|
|
1527
|
+
onChange: (text) => {
|
|
1528
|
+
otherText.setText(text);
|
|
1529
|
+
binding.setValue(text.trim() ? text : void 0);
|
|
1530
|
+
}
|
|
1531
|
+
}
|
|
1532
|
+
) : null
|
|
1533
|
+
] });
|
|
1534
|
+
}
|
|
1535
|
+
function NumberField(props) {
|
|
1536
|
+
const { binding } = props;
|
|
1537
|
+
const { min, max, step } = numberLimits(binding.field);
|
|
1538
|
+
const line = useLineClass();
|
|
1539
|
+
const shown = typeof binding.value === "number" ? String(binding.value) : typeof binding.value === "string" ? binding.value : "";
|
|
1540
|
+
const [text, setText] = useState(shown);
|
|
1541
|
+
useEffect(() => {
|
|
1542
|
+
setText((typed) => typed.trim() !== "" && Number(typed.trim().replace(",", ".")) === binding.value ? typed : shown);
|
|
1543
|
+
}, [shown, binding.value]);
|
|
1544
|
+
return /* @__PURE__ */ jsxs(FieldChrome, { props, children: [
|
|
1545
|
+
/* @__PURE__ */ jsx(
|
|
1546
|
+
Input,
|
|
1547
|
+
{
|
|
1548
|
+
...controlAria(props),
|
|
1549
|
+
type: "text",
|
|
1550
|
+
inputMode: min !== void 0 && min >= 0 && (step === void 0 || Number.isInteger(step)) ? "numeric" : "decimal",
|
|
1551
|
+
autoComplete: "off",
|
|
1552
|
+
value: text,
|
|
1553
|
+
placeholder: binding.field.placeholder,
|
|
1554
|
+
className: line,
|
|
1555
|
+
onChange: (e) => {
|
|
1556
|
+
const raw = e.target.value;
|
|
1557
|
+
setText(raw);
|
|
1558
|
+
const t = raw.trim().replace(",", ".");
|
|
1559
|
+
if (t === "") binding.setValue(void 0);
|
|
1560
|
+
else {
|
|
1561
|
+
const n = Number(t);
|
|
1562
|
+
binding.setValue(Number.isFinite(n) && /^[-+]?(\d+\.?\d*|\.\d+)(e[-+]?\d+)?$/i.test(t) ? n : raw);
|
|
1563
|
+
}
|
|
1564
|
+
}
|
|
1565
|
+
}
|
|
1566
|
+
),
|
|
1567
|
+
min !== void 0 || max !== void 0 ? /* @__PURE__ */ jsx("p", { className: "text-sm text-muted-foreground", "data-gf-number-range": "", children: min !== void 0 && max !== void 0 ? `From ${min} to ${max}` : min !== void 0 ? `${min} or more` : `${max} or less` }) : null
|
|
1568
|
+
] });
|
|
1569
|
+
}
|
|
1570
|
+
function DateField(props) {
|
|
1571
|
+
const { binding } = props;
|
|
1572
|
+
const { min, max } = dateLimits(binding.field);
|
|
1573
|
+
const line = useLineClass();
|
|
1574
|
+
return /* @__PURE__ */ jsx(FieldChrome, { props, children: /* @__PURE__ */ jsx(
|
|
1575
|
+
Input,
|
|
1576
|
+
{
|
|
1577
|
+
...controlAria(props),
|
|
1578
|
+
type: "date",
|
|
1579
|
+
min,
|
|
1580
|
+
max,
|
|
1581
|
+
value: typeof binding.value === "string" ? binding.value : "",
|
|
1582
|
+
className: cn("w-full sm:w-auto sm:min-w-48", line),
|
|
1583
|
+
onChange: (e) => binding.setValue(e.target.value === "" ? void 0 : e.target.value)
|
|
1584
|
+
}
|
|
1585
|
+
) });
|
|
1586
|
+
}
|
|
1587
|
+
function ConsentField(props) {
|
|
1588
|
+
const { binding, inputId } = props;
|
|
1589
|
+
const text = typeof binding.field.props?.text === "string" && binding.field.props.text.trim() ? binding.field.props.text : "I agree";
|
|
1590
|
+
const look = useChoiceLook(inputId, 1);
|
|
1591
|
+
return /* @__PURE__ */ jsx(FieldChrome, { props, children: /* @__PURE__ */ jsxs(Label, { htmlFor: `${inputId}-0`, className: look.row, children: [
|
|
1592
|
+
/* @__PURE__ */ jsx(
|
|
1593
|
+
Checkbox,
|
|
1594
|
+
{
|
|
1595
|
+
id: `${inputId}-0`,
|
|
1596
|
+
checked: binding.value === true,
|
|
1597
|
+
"aria-invalid": binding.error ? true : void 0,
|
|
1598
|
+
"aria-required": binding.field.required || void 0,
|
|
1599
|
+
onCheckedChange: (c) => binding.setValue(c === true ? true : void 0)
|
|
1600
|
+
}
|
|
1601
|
+
),
|
|
1602
|
+
/* @__PURE__ */ jsx("span", { children: props.engine.text(text) })
|
|
1050
1603
|
] }) });
|
|
1051
1604
|
}
|
|
1052
1605
|
var builtInRenderers = [
|
|
@@ -1061,21 +1614,128 @@ var builtInRenderers = [
|
|
|
1061
1614
|
{ type: "single-choice", Component: SingleChoiceField },
|
|
1062
1615
|
{ type: "multi-choice", Component: MultiChoiceField },
|
|
1063
1616
|
{ type: "yes-no", Component: YesNoField },
|
|
1064
|
-
|
|
1617
|
+
{ type: "dropdown", Component: DropdownField },
|
|
1618
|
+
{ type: "number", Component: NumberField },
|
|
1619
|
+
{ type: "date", Component: DateField },
|
|
1620
|
+
{ type: "consent", Component: ConsentField },
|
|
1621
|
+
...structuredRenderers,
|
|
1622
|
+
...calculatedRenderers,
|
|
1623
|
+
...photoRenderers
|
|
1065
1624
|
];
|
|
1066
1625
|
var defaultRegistry = createRendererRegistry(builtInRenderers);
|
|
1626
|
+
|
|
1627
|
+
// src/theme.ts
|
|
1628
|
+
function parseColor(value) {
|
|
1629
|
+
const v = value.trim().toLowerCase();
|
|
1630
|
+
let m = /^#([0-9a-f]{3})$/.exec(v);
|
|
1631
|
+
if (m) return [...m[1]].map((c) => parseInt(c + c, 16));
|
|
1632
|
+
m = /^#([0-9a-f]{6})$/.exec(v);
|
|
1633
|
+
if (m) return [0, 2, 4].map((i) => parseInt(m[1].slice(i, i + 2), 16));
|
|
1634
|
+
m = /^rgb\(\s*(\d{1,3})\s*,\s*(\d{1,3})\s*,\s*(\d{1,3})\s*\)$/.exec(v);
|
|
1635
|
+
if (m) {
|
|
1636
|
+
const rgb = [m[1], m[2], m[3]].map(Number);
|
|
1637
|
+
return rgb.every((c) => c <= 255) ? rgb : null;
|
|
1638
|
+
}
|
|
1639
|
+
return null;
|
|
1640
|
+
}
|
|
1641
|
+
function rgbToHsl([r, g, b]) {
|
|
1642
|
+
const [rn, gn, bn] = [r / 255, g / 255, b / 255];
|
|
1643
|
+
const max = Math.max(rn, gn, bn);
|
|
1644
|
+
const min = Math.min(rn, gn, bn);
|
|
1645
|
+
const l = (max + min) / 2;
|
|
1646
|
+
if (max === min) return [0, 0, l * 100];
|
|
1647
|
+
const d = max - min;
|
|
1648
|
+
const s = l > 0.5 ? d / (2 - max - min) : d / (max + min);
|
|
1649
|
+
const h = max === rn ? (gn - bn) / d + (gn < bn ? 6 : 0) : max === gn ? (bn - rn) / d + 2 : (rn - gn) / d + 4;
|
|
1650
|
+
return [h * 60, s * 100, l * 100];
|
|
1651
|
+
}
|
|
1652
|
+
function hslToRgb(h, s, l) {
|
|
1653
|
+
const sn = s / 100;
|
|
1654
|
+
const ln = l / 100;
|
|
1655
|
+
const k = (n) => (n + h / 30) % 12;
|
|
1656
|
+
const a = sn * Math.min(ln, 1 - ln);
|
|
1657
|
+
const f = (n) => ln - a * Math.max(-1, Math.min(k(n) - 3, Math.min(9 - k(n), 1)));
|
|
1658
|
+
return [f(0), f(8), f(4)].map((x) => Math.round(x * 255));
|
|
1659
|
+
}
|
|
1660
|
+
function luminance([r, g, b]) {
|
|
1661
|
+
const c = (x) => {
|
|
1662
|
+
const n = x / 255;
|
|
1663
|
+
return n <= 0.03928 ? n / 12.92 : ((n + 0.055) / 1.055) ** 2.4;
|
|
1664
|
+
};
|
|
1665
|
+
return 0.2126 * c(r) + 0.7152 * c(g) + 0.0722 * c(b);
|
|
1666
|
+
}
|
|
1667
|
+
function contrast(a, b) {
|
|
1668
|
+
const [x, y] = [luminance(a), luminance(b)].sort((p, q) => q - p);
|
|
1669
|
+
return (x + 0.05) / (y + 0.05);
|
|
1670
|
+
}
|
|
1671
|
+
var WHITE = [255, 255, 255];
|
|
1672
|
+
var NEAR_BLACK = hslToRgb(240, 10, 3.9);
|
|
1673
|
+
var WHITE_HSL = "0 0% 100%";
|
|
1674
|
+
var NEAR_BLACK_HSL = "240 10% 3.9%";
|
|
1675
|
+
var fmt = (h, s, l) => `${Math.round(h)} ${Math.round(s)}% ${Math.round(l * 10) / 10}%`;
|
|
1676
|
+
function fit(h, s, l, page, darken) {
|
|
1677
|
+
let light = l;
|
|
1678
|
+
for (let i = 0; i < 100 && contrast(hslToRgb(h, s, light), page) < 3; i++) light = Math.max(0, Math.min(100, light + (darken ? -1 : 1)));
|
|
1679
|
+
const best = () => {
|
|
1680
|
+
const rgb = hslToRgb(h, s, light);
|
|
1681
|
+
const white = contrast(rgb, WHITE);
|
|
1682
|
+
const dark = contrast(rgb, NEAR_BLACK);
|
|
1683
|
+
return white >= dark ? { ratio: white, fg: WHITE_HSL } : { ratio: dark, fg: NEAR_BLACK_HSL };
|
|
1684
|
+
};
|
|
1685
|
+
let pick = best();
|
|
1686
|
+
for (let i = 0; i < 100 && pick.ratio < 4.5; i++) {
|
|
1687
|
+
light = Math.min(100, light + 1);
|
|
1688
|
+
pick = { ratio: contrast(hslToRgb(h, s, light), NEAR_BLACK), fg: NEAR_BLACK_HSL };
|
|
1689
|
+
}
|
|
1690
|
+
return { primary: fmt(h, s, light), primaryForeground: pick.fg };
|
|
1691
|
+
}
|
|
1692
|
+
function themeColors(accent) {
|
|
1693
|
+
const rgb = parseColor(accent);
|
|
1694
|
+
if (!rgb) return null;
|
|
1695
|
+
const [h, s, l] = rgbToHsl(rgb);
|
|
1696
|
+
return { light: fit(h, s, l, WHITE, true), dark: fit(h, s, l, NEAR_BLACK, false) };
|
|
1697
|
+
}
|
|
1698
|
+
function themeCss(theme, selector) {
|
|
1699
|
+
const colors = theme?.accent ? themeColors(theme.accent) : null;
|
|
1700
|
+
if (!colors) return "";
|
|
1701
|
+
const block = (c) => `--primary: ${c.primary}; --primary-foreground: ${c.primaryForeground}; --ring: ${c.primary};`;
|
|
1702
|
+
return `${selector} { ${block(colors.light)} }
|
|
1703
|
+
.dark ${selector}, ${selector}.dark { ${block(colors.dark)} }`;
|
|
1704
|
+
}
|
|
1067
1705
|
function renderSlot(Default, override, props, extra) {
|
|
1068
1706
|
if (override === null) return null;
|
|
1069
1707
|
const Component = override ?? Default;
|
|
1070
1708
|
return /* @__PURE__ */ jsx(Component, { ...props, ...extra });
|
|
1071
1709
|
}
|
|
1072
1710
|
var focusRing = "outline-none focus-visible:ring-2 focus-visible:ring-ring focus-visible:ring-offset-2 ring-offset-background";
|
|
1073
|
-
function
|
|
1711
|
+
function RequiredNote({ text, className }) {
|
|
1712
|
+
return /* @__PURE__ */ jsxs("p", { className: cn("text-sm text-muted-foreground", className), "data-gf-required-note": "", children: [
|
|
1713
|
+
/* @__PURE__ */ jsxs("span", { className: "text-destructive", "aria-hidden": "true", children: [
|
|
1714
|
+
"*",
|
|
1715
|
+
" "
|
|
1716
|
+
] }),
|
|
1717
|
+
text
|
|
1718
|
+
] });
|
|
1719
|
+
}
|
|
1720
|
+
function HeaderImage({ image, className }) {
|
|
1721
|
+
return /* @__PURE__ */ jsx(
|
|
1722
|
+
"img",
|
|
1723
|
+
{
|
|
1724
|
+
src: image.src,
|
|
1725
|
+
alt: image.alt,
|
|
1726
|
+
"data-gf-header-image": "",
|
|
1727
|
+
className: cn("block h-32 w-full object-cover sm:h-44", className)
|
|
1728
|
+
}
|
|
1729
|
+
);
|
|
1730
|
+
}
|
|
1731
|
+
function DefaultHeader({ title, description, image, requiredNote, className }) {
|
|
1074
1732
|
const { presentation, layout } = useFormPresentation();
|
|
1075
1733
|
if (presentation === "page" && layout === "focus") {
|
|
1076
1734
|
return /* @__PURE__ */ jsxs("header", { className: cn("space-y-1", className), children: [
|
|
1735
|
+
image ? /* @__PURE__ */ jsx(HeaderImage, { image, className: "mb-4 rounded-lg" }) : null,
|
|
1077
1736
|
/* @__PURE__ */ jsx("h1", { className: "text-sm font-medium text-muted-foreground", children: title }),
|
|
1078
|
-
description ? /* @__PURE__ */ jsx("p", { className: "text-sm text-muted-foreground", children: description }) : null
|
|
1737
|
+
description ? /* @__PURE__ */ jsx("p", { className: "text-sm text-muted-foreground", children: description }) : null,
|
|
1738
|
+
requiredNote ? /* @__PURE__ */ jsx(RequiredNote, { text: requiredNote }) : null
|
|
1079
1739
|
] });
|
|
1080
1740
|
}
|
|
1081
1741
|
if (presentation === "page") {
|
|
@@ -1083,19 +1743,26 @@ function DefaultHeader({ title, description, className }) {
|
|
|
1083
1743
|
"header",
|
|
1084
1744
|
{
|
|
1085
1745
|
className: cn(
|
|
1086
|
-
"rounded-xl border
|
|
1746
|
+
"overflow-hidden rounded-xl border bg-card text-card-foreground",
|
|
1747
|
+
!image && "border-t-8 border-t-primary",
|
|
1087
1748
|
className
|
|
1088
1749
|
),
|
|
1089
1750
|
children: [
|
|
1090
|
-
/* @__PURE__ */ jsx(
|
|
1091
|
-
|
|
1751
|
+
image ? /* @__PURE__ */ jsx(HeaderImage, { image }) : null,
|
|
1752
|
+
/* @__PURE__ */ jsxs("div", { className: cn("px-5 py-6 sm:px-8 sm:py-8", image && "border-t-8 border-t-primary"), children: [
|
|
1753
|
+
/* @__PURE__ */ jsx("h1", { className: "text-3xl font-semibold tracking-tight text-balance sm:text-4xl", children: title }),
|
|
1754
|
+
description ? /* @__PURE__ */ jsx("p", { className: "mt-3 text-base text-muted-foreground text-pretty", children: description }) : null,
|
|
1755
|
+
requiredNote ? /* @__PURE__ */ jsx(RequiredNote, { text: requiredNote, className: "mt-4" }) : null
|
|
1756
|
+
] })
|
|
1092
1757
|
]
|
|
1093
1758
|
}
|
|
1094
1759
|
);
|
|
1095
1760
|
}
|
|
1096
1761
|
return /* @__PURE__ */ jsxs("header", { className: cn("space-y-1", className), children: [
|
|
1762
|
+
image ? /* @__PURE__ */ jsx(HeaderImage, { image, className: "mb-4 rounded-lg" }) : null,
|
|
1097
1763
|
/* @__PURE__ */ jsx("h1", { className: "text-2xl font-semibold tracking-tight", children: title }),
|
|
1098
|
-
description ? /* @__PURE__ */ jsx("p", { className: "text-muted-foreground", children: description }) : null
|
|
1764
|
+
description ? /* @__PURE__ */ jsx("p", { className: "text-muted-foreground", children: description }) : null,
|
|
1765
|
+
requiredNote ? /* @__PURE__ */ jsx(RequiredNote, { text: requiredNote, className: "pt-1" }) : null
|
|
1099
1766
|
] });
|
|
1100
1767
|
}
|
|
1101
1768
|
function initials(name) {
|
|
@@ -1297,6 +1964,8 @@ function DefaultErrorSummary({ count, errors, className }) {
|
|
|
1297
1964
|
);
|
|
1298
1965
|
}
|
|
1299
1966
|
var ADVANCE_DELAY_MS = 250;
|
|
1967
|
+
var PROGRESS_DELAY_MS = 400;
|
|
1968
|
+
var CLASSIC_PROGRESS_DELAY_MS = 1500;
|
|
1300
1969
|
var engineKeys = /* @__PURE__ */ new WeakMap();
|
|
1301
1970
|
var nextEngineKey = 0;
|
|
1302
1971
|
function keyOf(engine) {
|
|
@@ -1311,11 +1980,15 @@ function Form(props) {
|
|
|
1311
1980
|
return props.engine ? /* @__PURE__ */ jsx(FormWithEngine, { ...props, engine: props.engine }) : /* @__PURE__ */ jsx(OwnedForm, { ...props });
|
|
1312
1981
|
}
|
|
1313
1982
|
function OwnedForm(props) {
|
|
1314
|
-
const engine = useForm(props.form, props.options);
|
|
1983
|
+
const engine = useForm(props.form, props.hostOptions ? { ...props.options, hostOptions: props.hostOptions } : props.options);
|
|
1315
1984
|
return /* @__PURE__ */ jsx(FormWithEngine, { ...props, engine });
|
|
1316
1985
|
}
|
|
1317
1986
|
function FormWithEngine(props) {
|
|
1318
|
-
|
|
1987
|
+
const { onUpload } = props;
|
|
1988
|
+
const outer = useContext(UploadContext);
|
|
1989
|
+
const host = useMemo(() => onUpload ? { upload: onUpload } : outer, [onUpload, outer]);
|
|
1990
|
+
const [pending] = useState(createPendingUploads);
|
|
1991
|
+
return /* @__PURE__ */ jsx(UploadContext.Provider, { value: host, children: /* @__PURE__ */ jsx(PendingUploadsContext.Provider, { value: pending, children: /* @__PURE__ */ jsx(FormRuntime, { engine: props.engine, registry: props.registry ?? defaultRegistry, children: /* @__PURE__ */ jsx(FormBody, { ...props, pendingUploads: pending }, keyOf(props.engine)) }) }) });
|
|
1319
1992
|
}
|
|
1320
1993
|
function focusWithin(root) {
|
|
1321
1994
|
if (!root) return;
|
|
@@ -1328,16 +2001,21 @@ function FormBody({
|
|
|
1328
2001
|
layout: layoutProp,
|
|
1329
2002
|
presentation = "embedded",
|
|
1330
2003
|
brand,
|
|
2004
|
+
theme,
|
|
1331
2005
|
onSubmit,
|
|
1332
2006
|
onChange,
|
|
2007
|
+
onProgress,
|
|
2008
|
+
progressNotice = "Your answers save as you go.",
|
|
1333
2009
|
autoAdvance = true,
|
|
1334
|
-
submitLabel = "
|
|
2010
|
+
submitLabel = "Send",
|
|
2011
|
+
requiredLabel = "Required",
|
|
1335
2012
|
nextLabel = "OK",
|
|
1336
2013
|
retryLabel = "Try again",
|
|
1337
2014
|
slots,
|
|
1338
2015
|
slotProps,
|
|
1339
2016
|
className,
|
|
1340
|
-
testID
|
|
2017
|
+
testID,
|
|
2018
|
+
pendingUploads
|
|
1341
2019
|
}) {
|
|
1342
2020
|
const { idPrefix } = useFormContext();
|
|
1343
2021
|
const def = engine.definition;
|
|
@@ -1372,6 +2050,52 @@ function FormBody({
|
|
|
1372
2050
|
if (!onChange) return;
|
|
1373
2051
|
return engine.subscribe(() => onChange(engine.getState(), engine));
|
|
1374
2052
|
}, [engine, onChange]);
|
|
2053
|
+
const saveProgress = def.settings?.saveProgress === true && !!onProgress;
|
|
2054
|
+
const onProgressRef = useRef(onProgress);
|
|
2055
|
+
onProgressRef.current = onProgress;
|
|
2056
|
+
useEffect(() => {
|
|
2057
|
+
if (!saveProgress) return;
|
|
2058
|
+
let timer;
|
|
2059
|
+
let last = engine.getState();
|
|
2060
|
+
const fire = () => {
|
|
2061
|
+
timer = void 0;
|
|
2062
|
+
if (engine.getState().status === "completed") return;
|
|
2063
|
+
try {
|
|
2064
|
+
void Promise.resolve(onProgressRef.current?.(engine, formProgress(engine))).catch(() => {
|
|
2065
|
+
});
|
|
2066
|
+
} catch {
|
|
2067
|
+
}
|
|
2068
|
+
};
|
|
2069
|
+
const schedule = (delay) => {
|
|
2070
|
+
clearTimeout(timer);
|
|
2071
|
+
timer = setTimeout(fire, delay);
|
|
2072
|
+
};
|
|
2073
|
+
const unsubscribe = engine.subscribe(() => {
|
|
2074
|
+
const state = engine.getState();
|
|
2075
|
+
const previous = last;
|
|
2076
|
+
last = state;
|
|
2077
|
+
if (state.status === "completed") {
|
|
2078
|
+
clearTimeout(timer);
|
|
2079
|
+
timer = void 0;
|
|
2080
|
+
return;
|
|
2081
|
+
}
|
|
2082
|
+
const event = state.events[state.events.length - 1];
|
|
2083
|
+
if (layout === "focus") {
|
|
2084
|
+
if (event?.type === "next" && state.current !== previous.current) schedule(PROGRESS_DELAY_MS);
|
|
2085
|
+
} else if (event?.type === "answer") schedule(CLASSIC_PROGRESS_DELAY_MS);
|
|
2086
|
+
});
|
|
2087
|
+
const flush = () => {
|
|
2088
|
+
if (timer === void 0) return;
|
|
2089
|
+
clearTimeout(timer);
|
|
2090
|
+
fire();
|
|
2091
|
+
};
|
|
2092
|
+
window.addEventListener("pagehide", flush);
|
|
2093
|
+
return () => {
|
|
2094
|
+
unsubscribe();
|
|
2095
|
+
window.removeEventListener("pagehide", flush);
|
|
2096
|
+
flush();
|
|
2097
|
+
};
|
|
2098
|
+
}, [engine, saveProgress, layout]);
|
|
1375
2099
|
useLayoutEffect(() => {
|
|
1376
2100
|
if (previousCurrent.current === current) return;
|
|
1377
2101
|
previousCurrent.current = current;
|
|
@@ -1400,8 +2124,14 @@ function FormBody({
|
|
|
1400
2124
|
const message = state.current ? state.errors[state.current] : void 0;
|
|
1401
2125
|
if (message) announce(message);
|
|
1402
2126
|
};
|
|
2127
|
+
const waitingForUploads = () => {
|
|
2128
|
+
if (pendingUploads.count() === 0) return false;
|
|
2129
|
+
announce(UPLOAD_WAIT_MESSAGE);
|
|
2130
|
+
return true;
|
|
2131
|
+
};
|
|
1403
2132
|
const finish = async () => {
|
|
1404
2133
|
if (sending.current) return;
|
|
2134
|
+
if (engine.getState().status !== "completed" && waitingForUploads()) return;
|
|
1405
2135
|
if (engine.getState().status !== "completed" && !engine.submit()) {
|
|
1406
2136
|
if (layout === "classic") setSummaryRequest((n) => n + 1);
|
|
1407
2137
|
else announceStepError();
|
|
@@ -1424,6 +2154,7 @@ function FormBody({
|
|
|
1424
2154
|
};
|
|
1425
2155
|
const isLastStep = (state) => state.current === state.path[state.path.length - 1];
|
|
1426
2156
|
const advance = () => {
|
|
2157
|
+
if (waitingForUploads()) return;
|
|
1427
2158
|
direction.current = "forward";
|
|
1428
2159
|
if (isLastStep(engine.getState())) void finish();
|
|
1429
2160
|
else if (!engine.next()) announceStepError();
|
|
@@ -1468,6 +2199,10 @@ function FormBody({
|
|
|
1468
2199
|
};
|
|
1469
2200
|
const liveRegion = /* @__PURE__ */ jsx("div", { role: "status", "aria-live": "polite", "aria-atomic": "true", className: "sr-only", "data-gf-live": "", children: announcement });
|
|
1470
2201
|
const presentationValue = { presentation, layout };
|
|
2202
|
+
const themeId = `gf-theme-${useId().replace(/[^a-zA-Z0-9_-]/g, "")}`;
|
|
2203
|
+
const themeStyle = themeCss(theme, `[data-gf-theme="${themeId}"]`);
|
|
2204
|
+
const themed = themeStyle ? { "data-gf-theme": themeId } : {};
|
|
2205
|
+
const styleNode = themeStyle ? /* @__PURE__ */ jsx("style", { children: themeStyle }) : null;
|
|
1471
2206
|
const brandNode = brand ? renderSlot(DefaultBrand, slots?.brand, { name: brand.name, logo: brand.logo }, slotProps?.brand) : null;
|
|
1472
2207
|
if (showEnding) {
|
|
1473
2208
|
const end = def.endings?.find((x) => x.id === ending) ?? def.endings?.[0];
|
|
@@ -1482,10 +2217,12 @@ function FormBody({
|
|
|
1482
2217
|
);
|
|
1483
2218
|
return /* @__PURE__ */ jsxs(FormPresentationProvider, { value: presentationValue, children: [
|
|
1484
2219
|
liveRegion,
|
|
2220
|
+
styleNode,
|
|
1485
2221
|
page ? /* @__PURE__ */ jsxs(
|
|
1486
2222
|
"div",
|
|
1487
2223
|
{
|
|
1488
2224
|
ref: endingRef,
|
|
2225
|
+
...themed,
|
|
1489
2226
|
"data-presentation": "page",
|
|
1490
2227
|
"data-testid": testID,
|
|
1491
2228
|
className: cn("flex min-h-dvh w-full flex-col bg-background text-foreground", className),
|
|
@@ -1494,7 +2231,7 @@ function FormBody({
|
|
|
1494
2231
|
/* @__PURE__ */ jsx("div", { className: "flex flex-1 items-center justify-center px-4 pb-24 pt-8 sm:px-6", children: /* @__PURE__ */ jsx("div", { className: "w-full max-w-xl", children: endingNode }) })
|
|
1495
2232
|
]
|
|
1496
2233
|
}
|
|
1497
|
-
) : /* @__PURE__ */ jsxs("div", { ref: endingRef, className: cn("mx-auto w-full max-w-xl space-y-6", className), "data-testid": testID, children: [
|
|
2234
|
+
) : /* @__PURE__ */ jsxs("div", { ref: endingRef, ...themed, className: cn("mx-auto w-full max-w-xl space-y-6", className), "data-testid": testID, children: [
|
|
1498
2235
|
brandNode,
|
|
1499
2236
|
endingNode
|
|
1500
2237
|
] })
|
|
@@ -1502,6 +2239,7 @@ function FormBody({
|
|
|
1502
2239
|
}
|
|
1503
2240
|
const pageFocus = page && layout === "focus";
|
|
1504
2241
|
const pageClassic = page && layout === "classic";
|
|
2242
|
+
const notice = saveProgress && progressNotice && historyLength === 0 ? /* @__PURE__ */ jsx("p", { className: "text-sm text-muted-foreground", "data-gf-progress-notice": "", children: progressNotice }) : null;
|
|
1505
2243
|
const renderBlock = (id, focus) => {
|
|
1506
2244
|
const block = engine.block(id);
|
|
1507
2245
|
if (!block || block.kind === "page-break") return null;
|
|
@@ -1543,10 +2281,16 @@ function FormBody({
|
|
|
1543
2281
|
}));
|
|
1544
2282
|
const currentField = pageFocus && current ? engine.field(current) : void 0;
|
|
1545
2283
|
const keyHint = currentField ? currentField.type === "long-text" ? `${modKey} + Enter` : "Enter \u21B5" : void 0;
|
|
2284
|
+
const hasRequired = def.blocks.some((b) => b.kind === "field" && b.required === true);
|
|
1546
2285
|
const header = renderSlot(
|
|
1547
2286
|
DefaultHeader,
|
|
1548
2287
|
slots?.header,
|
|
1549
|
-
{
|
|
2288
|
+
{
|
|
2289
|
+
title: def.title,
|
|
2290
|
+
description,
|
|
2291
|
+
image: theme?.headerImage ? { src: theme.headerImage, alt: theme.headerImageAlt ?? "" } : void 0,
|
|
2292
|
+
requiredNote: hasRequired && requiredLabel ? requiredLabel : void 0
|
|
2293
|
+
},
|
|
1550
2294
|
slotProps?.header
|
|
1551
2295
|
);
|
|
1552
2296
|
const errorSummary = errorItems.length > 0 && layout === "classic" ? renderSlot(
|
|
@@ -1602,6 +2346,7 @@ function FormBody({
|
|
|
1602
2346
|
slotProps?.navigation
|
|
1603
2347
|
);
|
|
1604
2348
|
const formProps = {
|
|
2349
|
+
...themed,
|
|
1605
2350
|
ref: formRef,
|
|
1606
2351
|
noValidate: true,
|
|
1607
2352
|
onSubmit: handleSubmit,
|
|
@@ -1612,6 +2357,7 @@ function FormBody({
|
|
|
1612
2357
|
};
|
|
1613
2358
|
return /* @__PURE__ */ jsxs(FormPresentationProvider, { value: presentationValue, children: [
|
|
1614
2359
|
liveRegion,
|
|
2360
|
+
styleNode,
|
|
1615
2361
|
pageFocus ? /* @__PURE__ */ jsxs(
|
|
1616
2362
|
"form",
|
|
1617
2363
|
{
|
|
@@ -1623,6 +2369,7 @@ function FormBody({
|
|
|
1623
2369
|
/* @__PURE__ */ jsx("div", { className: "flex flex-1 flex-col justify-center px-4 pb-20 pt-4 sm:px-6 sm:pb-28", children: /* @__PURE__ */ jsxs("div", { className: "mx-auto w-full max-w-xl", children: [
|
|
1624
2370
|
/* @__PURE__ */ jsx("div", { className: historyLength === 0 ? "mb-5" : "sr-only", children: header }),
|
|
1625
2371
|
fields,
|
|
2372
|
+
notice ? /* @__PURE__ */ jsx("div", { className: "mt-4", children: notice }) : null,
|
|
1626
2373
|
failure ? /* @__PURE__ */ jsx("div", { className: "mt-6", children: failure }) : null,
|
|
1627
2374
|
/* @__PURE__ */ jsx("div", { className: "mt-10", children: navigation })
|
|
1628
2375
|
] }) })
|
|
@@ -1636,6 +2383,7 @@ function FormBody({
|
|
|
1636
2383
|
children: /* @__PURE__ */ jsxs("div", { className: "mx-auto w-full max-w-2xl space-y-4", children: [
|
|
1637
2384
|
brandNode ? /* @__PURE__ */ jsx("div", { className: "flex justify-center pb-2", children: brandNode }) : null,
|
|
1638
2385
|
header,
|
|
2386
|
+
notice,
|
|
1639
2387
|
errorSummary,
|
|
1640
2388
|
fields,
|
|
1641
2389
|
failure,
|
|
@@ -1645,9 +2393,11 @@ function FormBody({
|
|
|
1645
2393
|
) : /* @__PURE__ */ jsxs("form", { ...formProps, className: cn("mx-auto w-full max-w-xl space-y-8", className), children: [
|
|
1646
2394
|
brandNode,
|
|
1647
2395
|
header,
|
|
2396
|
+
layout === "classic" ? notice : null,
|
|
1648
2397
|
errorSummary,
|
|
1649
2398
|
progress,
|
|
1650
2399
|
fields,
|
|
2400
|
+
layout === "focus" ? notice : null,
|
|
1651
2401
|
failure,
|
|
1652
2402
|
navigation
|
|
1653
2403
|
] })
|
|
@@ -1655,6 +2405,6 @@ function FormBody({
|
|
|
1655
2405
|
}
|
|
1656
2406
|
var classicSection = "rounded-xl border bg-card px-5 py-6 text-card-foreground sm:px-8 has-[[aria-invalid=true]]:border-destructive";
|
|
1657
2407
|
|
|
1658
|
-
export { FieldChrome, Form, MediaTimeContext, SourceTextContext, builtInRenderers, defaultRegistry, describedBy, useFormPresentation, useMediaTime, useSourceText };
|
|
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 };
|
|
1659
2409
|
//# sourceMappingURL=index.js.map
|
|
1660
2410
|
//# sourceMappingURL=index.js.map
|