@usefillo/react 0.11.0 → 0.12.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/index.d.ts +85 -13
- package/dist/index.js +469 -252
- package/dist/styles.css +161 -36
- package/dist/styles.unlayered.css +161 -36
- package/package.json +3 -3
package/dist/index.js
CHANGED
|
@@ -15,7 +15,6 @@ import {
|
|
|
15
15
|
createClient,
|
|
16
16
|
formSchemasEqual,
|
|
17
17
|
isBuildTimeDevEnv as isBuildTimeDevEnv2,
|
|
18
|
-
isField as isField2,
|
|
19
18
|
isFilloError as isFilloError2,
|
|
20
19
|
isLikelyDevEnv,
|
|
21
20
|
needsExplicitSubmit,
|
|
@@ -25,7 +24,7 @@ import {
|
|
|
25
24
|
reachablePageSequence,
|
|
26
25
|
resolveThemeAppearance
|
|
27
26
|
} from "@usefillo/core";
|
|
28
|
-
import {
|
|
27
|
+
import { resolveStrings, slotClass as slotClass3 } from "@usefillo/core";
|
|
29
28
|
|
|
30
29
|
// src/appearance.tsx
|
|
31
30
|
import { createContext, useContext } from "react";
|
|
@@ -483,10 +482,12 @@ import {
|
|
|
483
482
|
formatGroupedNumber,
|
|
484
483
|
isField,
|
|
485
484
|
isValidPartialNumberText,
|
|
485
|
+
localeForNotation,
|
|
486
486
|
parseGroupedNumber,
|
|
487
487
|
pipeBlock,
|
|
488
488
|
radioGroupStep,
|
|
489
489
|
REQUIRED_FIELD_MESSAGE,
|
|
490
|
+
requiredFieldMessage,
|
|
490
491
|
shouldAutoSubmit,
|
|
491
492
|
slotClass as slotClass2,
|
|
492
493
|
visibleFields
|
|
@@ -1007,7 +1008,84 @@ function uploadFailureMessage(error, strings) {
|
|
|
1007
1008
|
}
|
|
1008
1009
|
return strings.uploadFailed;
|
|
1009
1010
|
}
|
|
1010
|
-
function
|
|
1011
|
+
function FileStateIcon({ state }) {
|
|
1012
|
+
if (state === "done") {
|
|
1013
|
+
return /* @__PURE__ */ jsx4("span", { className: "fillo-file-state fillo-file-state--done", "aria-hidden": "true", children: /* @__PURE__ */ jsxs4(
|
|
1014
|
+
"svg",
|
|
1015
|
+
{
|
|
1016
|
+
className: "fillo-file-state-icon",
|
|
1017
|
+
viewBox: "0 0 24 24",
|
|
1018
|
+
width: "22",
|
|
1019
|
+
height: "22",
|
|
1020
|
+
fill: "none",
|
|
1021
|
+
"aria-hidden": "true",
|
|
1022
|
+
focusable: "false",
|
|
1023
|
+
children: [
|
|
1024
|
+
/* @__PURE__ */ jsx4("path", { d: "M3.75 12a8.25 8.25 0 1 0 16.5 0 8.25 8.25 0 1 0-16.5 0" }),
|
|
1025
|
+
/* @__PURE__ */ jsx4("path", { d: "m8.5 12 2.25 2.25 4.75-5" })
|
|
1026
|
+
]
|
|
1027
|
+
}
|
|
1028
|
+
) });
|
|
1029
|
+
}
|
|
1030
|
+
if (state === "failed") {
|
|
1031
|
+
return /* @__PURE__ */ jsx4("span", { className: "fillo-file-state fillo-file-state--failed", "aria-hidden": "true", children: /* @__PURE__ */ jsxs4(
|
|
1032
|
+
"svg",
|
|
1033
|
+
{
|
|
1034
|
+
className: "fillo-file-state-icon",
|
|
1035
|
+
viewBox: "0 0 24 24",
|
|
1036
|
+
width: "22",
|
|
1037
|
+
height: "22",
|
|
1038
|
+
fill: "none",
|
|
1039
|
+
"aria-hidden": "true",
|
|
1040
|
+
focusable: "false",
|
|
1041
|
+
children: [
|
|
1042
|
+
/* @__PURE__ */ jsx4("path", { d: "M3.75 12a8.25 8.25 0 1 0 16.5 0 8.25 8.25 0 1 0-16.5 0" }),
|
|
1043
|
+
/* @__PURE__ */ jsx4("path", { d: "M12 7.75v5.5" }),
|
|
1044
|
+
/* @__PURE__ */ jsx4("path", { d: "M12 16.5h.01" })
|
|
1045
|
+
]
|
|
1046
|
+
}
|
|
1047
|
+
) });
|
|
1048
|
+
}
|
|
1049
|
+
return /* @__PURE__ */ jsx4("span", { className: "fillo-file-state fillo-file-state--uploading", "aria-hidden": "true", children: /* @__PURE__ */ jsxs4(
|
|
1050
|
+
"svg",
|
|
1051
|
+
{
|
|
1052
|
+
className: "fillo-file-state-icon",
|
|
1053
|
+
viewBox: "0 0 24 24",
|
|
1054
|
+
width: "22",
|
|
1055
|
+
height: "22",
|
|
1056
|
+
fill: "none",
|
|
1057
|
+
"aria-hidden": "true",
|
|
1058
|
+
focusable: "false",
|
|
1059
|
+
children: [
|
|
1060
|
+
/* @__PURE__ */ jsx4("path", { d: "M7.25 3.75h6.5l3 3v13.5h-9.5z" }),
|
|
1061
|
+
/* @__PURE__ */ jsx4("path", { d: "M13.75 3.75v3h3" })
|
|
1062
|
+
]
|
|
1063
|
+
}
|
|
1064
|
+
) });
|
|
1065
|
+
}
|
|
1066
|
+
function CloseIcon() {
|
|
1067
|
+
return /* @__PURE__ */ jsx4(
|
|
1068
|
+
"svg",
|
|
1069
|
+
{
|
|
1070
|
+
className: "fillo-file-action-icon",
|
|
1071
|
+
viewBox: "0 0 24 24",
|
|
1072
|
+
width: "16",
|
|
1073
|
+
height: "16",
|
|
1074
|
+
fill: "none",
|
|
1075
|
+
"aria-hidden": "true",
|
|
1076
|
+
focusable: "false",
|
|
1077
|
+
children: /* @__PURE__ */ jsx4("path", { d: "m7 7 10 10M17 7 7 17" })
|
|
1078
|
+
}
|
|
1079
|
+
);
|
|
1080
|
+
}
|
|
1081
|
+
function FileUploadField({
|
|
1082
|
+
field,
|
|
1083
|
+
value,
|
|
1084
|
+
error,
|
|
1085
|
+
setValue,
|
|
1086
|
+
api,
|
|
1087
|
+
ids: providedIds
|
|
1088
|
+
}) {
|
|
1011
1089
|
const ids = useFilloFieldIds(field.id, providedIds);
|
|
1012
1090
|
const slots = useFieldSlots(field, error);
|
|
1013
1091
|
const strings = useStrings();
|
|
@@ -1036,9 +1114,10 @@ function FileUploadField({ field, value, error, setValue, api, ids: providedIds
|
|
|
1036
1114
|
const configuredMaxMb = schema.maxFileSizeMb ?? 500;
|
|
1037
1115
|
const maxMb = chrome?.uploadFileSizeLimitMb ? Math.min(configuredMaxMb, chrome.uploadFileSizeLimitMb) : configuredMaxMb;
|
|
1038
1116
|
const maxBytes = maxMb * 1024 * 1024;
|
|
1117
|
+
const renderOnly = chrome?.renderOnly === true;
|
|
1039
1118
|
const storageBlocked = chrome?.uploadsAvailable === false;
|
|
1040
1119
|
const storageUrl = storageBlocked && chrome?.devChrome ? safeHttpUrl(chrome.warningUrl) : null;
|
|
1041
|
-
const canUpload = Boolean(api.client && api.formId) && !storageBlocked;
|
|
1120
|
+
const canUpload = !renderOnly && Boolean(api.client && api.formId) && !storageBlocked;
|
|
1042
1121
|
const activeInFlight = inFlight.filter((f) => !f.error).length;
|
|
1043
1122
|
const remaining = maxFiles - files.length - activeInFlight;
|
|
1044
1123
|
async function startUpload(file) {
|
|
@@ -1047,7 +1126,15 @@ function FileUploadField({ field, value, error, setValue, api, ids: providedIds
|
|
|
1047
1126
|
if (file.size > maxBytes) {
|
|
1048
1127
|
setInFlight((prev) => [
|
|
1049
1128
|
...prev,
|
|
1050
|
-
{
|
|
1129
|
+
{
|
|
1130
|
+
key,
|
|
1131
|
+
name: file.name,
|
|
1132
|
+
size: file.size,
|
|
1133
|
+
fraction: 0,
|
|
1134
|
+
file,
|
|
1135
|
+
tooLarge: true,
|
|
1136
|
+
error: strings.fileTooLarge(maxMb)
|
|
1137
|
+
}
|
|
1051
1138
|
]);
|
|
1052
1139
|
return;
|
|
1053
1140
|
}
|
|
@@ -1074,9 +1161,7 @@ function FileUploadField({ field, value, error, setValue, api, ids: providedIds
|
|
|
1074
1161
|
} catch (err) {
|
|
1075
1162
|
if (mountedRef.current) {
|
|
1076
1163
|
const message = uploadFailureMessage(err, strings);
|
|
1077
|
-
setInFlight(
|
|
1078
|
-
(prev) => prev.map((f) => f.key === key ? { ...f, error: message } : f)
|
|
1079
|
-
);
|
|
1164
|
+
setInFlight((prev) => prev.map((f) => f.key === key ? { ...f, error: message } : f));
|
|
1080
1165
|
}
|
|
1081
1166
|
} finally {
|
|
1082
1167
|
controllers.current.delete(key);
|
|
@@ -1100,109 +1185,111 @@ function FileUploadField({ field, value, error, setValue, api, ids: providedIds
|
|
|
1100
1185
|
setValue(next);
|
|
1101
1186
|
}
|
|
1102
1187
|
const describedBy = [field.description ? ids.descriptionId : null, error ? ids.errorId : null].filter(Boolean).join(" ") || void 0;
|
|
1103
|
-
return /* @__PURE__ */ jsxs4(
|
|
1104
|
-
|
|
1105
|
-
|
|
1106
|
-
|
|
1107
|
-
|
|
1108
|
-
|
|
1109
|
-
|
|
1110
|
-
|
|
1111
|
-
|
|
1112
|
-
|
|
1113
|
-
|
|
1114
|
-
className: slots.
|
|
1115
|
-
|
|
1116
|
-
|
|
1117
|
-
|
|
1118
|
-
|
|
1119
|
-
|
|
1120
|
-
|
|
1121
|
-
|
|
1122
|
-
|
|
1123
|
-
|
|
1124
|
-
|
|
1125
|
-
|
|
1126
|
-
|
|
1127
|
-
|
|
1128
|
-
|
|
1129
|
-
|
|
1130
|
-
|
|
1131
|
-
|
|
1132
|
-
|
|
1133
|
-
|
|
1134
|
-
|
|
1135
|
-
|
|
1136
|
-
|
|
1188
|
+
return /* @__PURE__ */ jsxs4(
|
|
1189
|
+
"div",
|
|
1190
|
+
{
|
|
1191
|
+
...slots.wrapperProps(
|
|
1192
|
+
`fillo-field fillo-field--file_upload${error ? " fillo-field--error" : ""}`
|
|
1193
|
+
),
|
|
1194
|
+
children: [
|
|
1195
|
+
/* @__PURE__ */ jsxs4("label", { className: slots.label, "data-fillo": "label", id: ids.labelId, htmlFor: ids.inputId, children: [
|
|
1196
|
+
field.label,
|
|
1197
|
+
!field.required && /* @__PURE__ */ jsx4("span", { className: "fillo-optional", children: strings.optional })
|
|
1198
|
+
] }),
|
|
1199
|
+
field.description && /* @__PURE__ */ jsx4("p", { className: slots.description, "data-fillo": "fieldDescription", id: ids.descriptionId, children: field.description }),
|
|
1200
|
+
remaining > 0 && /* @__PURE__ */ jsxs4(
|
|
1201
|
+
"div",
|
|
1202
|
+
{
|
|
1203
|
+
"data-fillo": "control",
|
|
1204
|
+
"data-drag-over": dragOver ? "" : void 0,
|
|
1205
|
+
className: slots.control(
|
|
1206
|
+
`fillo-dropzone${dragOver ? " fillo-dropzone--over" : ""}${canUpload ? "" : " fillo-dropzone--disabled"}`
|
|
1207
|
+
),
|
|
1208
|
+
onDragOver: (e) => {
|
|
1209
|
+
e.preventDefault();
|
|
1210
|
+
setDragOver(true);
|
|
1211
|
+
},
|
|
1212
|
+
onDragLeave: () => setDragOver(false),
|
|
1213
|
+
onDrop: (e) => {
|
|
1214
|
+
e.preventDefault();
|
|
1215
|
+
setDragOver(false);
|
|
1216
|
+
if (canUpload) handleFiles(e.dataTransfer.files);
|
|
1217
|
+
},
|
|
1218
|
+
onClick: () => canUpload && inputRef.current?.click(),
|
|
1219
|
+
role: "button",
|
|
1220
|
+
tabIndex: 0,
|
|
1221
|
+
"aria-disabled": !canUpload || void 0,
|
|
1222
|
+
"aria-labelledby": ids.labelId,
|
|
1223
|
+
"aria-invalid": error ? true : void 0,
|
|
1224
|
+
"aria-describedby": describedBy,
|
|
1225
|
+
onKeyDown: (e) => {
|
|
1226
|
+
if (e.key === "Enter" || e.key === " ") {
|
|
1227
|
+
e.preventDefault();
|
|
1228
|
+
if (canUpload) inputRef.current?.click();
|
|
1229
|
+
}
|
|
1230
|
+
},
|
|
1231
|
+
children: [
|
|
1232
|
+
/* @__PURE__ */ jsx4(
|
|
1233
|
+
"input",
|
|
1234
|
+
{
|
|
1235
|
+
ref: inputRef,
|
|
1236
|
+
id: ids.inputId,
|
|
1237
|
+
type: "file",
|
|
1238
|
+
hidden: true,
|
|
1239
|
+
disabled: !canUpload,
|
|
1240
|
+
multiple: maxFiles > 1,
|
|
1241
|
+
accept: schema.accept?.join(","),
|
|
1242
|
+
onChange: (e) => canUpload && handleFiles(e.target.files)
|
|
1243
|
+
}
|
|
1244
|
+
),
|
|
1245
|
+
canUpload ? /* @__PURE__ */ jsxs4(Fragment2, { children: [
|
|
1246
|
+
/* @__PURE__ */ jsx4("span", { className: "fillo-dropzone-title", children: strings.dropzoneTitle(maxFiles > 1) }),
|
|
1247
|
+
/* @__PURE__ */ jsx4("span", { className: "fillo-dropzone-hint", children: strings.dropzoneHint(maxMb) })
|
|
1248
|
+
] }) : storageBlocked && chrome?.devChrome ? /* @__PURE__ */ jsxs4("span", { className: "fillo-dropzone-hint", children: [
|
|
1249
|
+
"Connect file storage to enable uploads",
|
|
1250
|
+
storageUrl && /* @__PURE__ */ jsxs4(Fragment2, { children: [
|
|
1251
|
+
" \u2014 ",
|
|
1252
|
+
/* @__PURE__ */ jsx4("a", { href: storageUrl, target: "_blank", rel: "noopener noreferrer", children: storageUrl })
|
|
1253
|
+
] })
|
|
1254
|
+
] }) : /* @__PURE__ */ jsx4("span", { className: "fillo-dropzone-hint", children: renderOnly ? strings.uploadsRenderOnly : storageBlocked ? strings.uploadsUnavailable : strings.uploadsDisabled })
|
|
1255
|
+
]
|
|
1137
1256
|
}
|
|
1138
|
-
|
|
1139
|
-
children: [
|
|
1140
|
-
/* @__PURE__ */ jsx4(
|
|
1141
|
-
"input",
|
|
1142
|
-
{
|
|
1143
|
-
ref: inputRef,
|
|
1144
|
-
id: ids.inputId,
|
|
1145
|
-
type: "file",
|
|
1146
|
-
hidden: true,
|
|
1147
|
-
disabled: !canUpload,
|
|
1148
|
-
multiple: maxFiles > 1,
|
|
1149
|
-
accept: schema.accept?.join(","),
|
|
1150
|
-
onChange: (e) => canUpload && handleFiles(e.target.files)
|
|
1151
|
-
}
|
|
1152
|
-
),
|
|
1153
|
-
canUpload ? /* @__PURE__ */ jsxs4(Fragment2, { children: [
|
|
1154
|
-
/* @__PURE__ */ jsx4("span", { className: "fillo-dropzone-title", children: strings.dropzoneTitle(maxFiles > 1) }),
|
|
1155
|
-
/* @__PURE__ */ jsx4("span", { className: "fillo-dropzone-hint", children: strings.dropzoneHint(maxMb) })
|
|
1156
|
-
] }) : storageBlocked && chrome?.devChrome ? /* @__PURE__ */ jsxs4("span", { className: "fillo-dropzone-hint", children: [
|
|
1157
|
-
"Connect file storage to enable uploads",
|
|
1158
|
-
storageUrl && /* @__PURE__ */ jsxs4(Fragment2, { children: [
|
|
1159
|
-
" \u2014 ",
|
|
1160
|
-
/* @__PURE__ */ jsx4("a", { href: storageUrl, target: "_blank", rel: "noopener noreferrer", children: storageUrl })
|
|
1161
|
-
] })
|
|
1162
|
-
] }) : /* @__PURE__ */ jsx4("span", { className: "fillo-dropzone-hint", children: storageBlocked ? strings.uploadsUnavailable : strings.uploadsDisabled })
|
|
1163
|
-
]
|
|
1164
|
-
}
|
|
1165
|
-
),
|
|
1166
|
-
/* @__PURE__ */ jsx4(
|
|
1167
|
-
"div",
|
|
1168
|
-
{
|
|
1169
|
-
role: "status",
|
|
1170
|
-
"aria-live": "polite",
|
|
1171
|
-
className: "fillo-sr-only",
|
|
1172
|
-
style: { position: "absolute", left: "-9999px", width: 1, height: 1, overflow: "hidden" },
|
|
1173
|
-
children: uploadStatus(inFlight, files.length, strings)
|
|
1174
|
-
}
|
|
1175
|
-
),
|
|
1176
|
-
(inFlight.length > 0 || files.length > 0) && /* @__PURE__ */ jsxs4("ul", { className: "fillo-files", children: [
|
|
1177
|
-
files.map((f) => /* @__PURE__ */ jsxs4("li", { className: "fillo-file fillo-file--done", children: [
|
|
1178
|
-
/* @__PURE__ */ jsx4("span", { className: "fillo-file-name", children: f.name }),
|
|
1179
|
-
/* @__PURE__ */ jsx4("span", { className: "fillo-file-meta", children: formatBytes(f.size) }),
|
|
1180
|
-
/* @__PURE__ */ jsx4("span", { className: "fillo-sr-only", children: "Uploaded" }),
|
|
1257
|
+
),
|
|
1181
1258
|
/* @__PURE__ */ jsx4(
|
|
1182
|
-
"
|
|
1259
|
+
"div",
|
|
1183
1260
|
{
|
|
1184
|
-
|
|
1185
|
-
|
|
1186
|
-
"
|
|
1187
|
-
|
|
1188
|
-
children:
|
|
1261
|
+
role: "status",
|
|
1262
|
+
"aria-live": "polite",
|
|
1263
|
+
className: "fillo-sr-only",
|
|
1264
|
+
style: { position: "absolute", left: "-9999px", width: 1, height: 1, overflow: "hidden" },
|
|
1265
|
+
children: uploadStatus(inFlight, files.length, strings)
|
|
1189
1266
|
}
|
|
1190
|
-
)
|
|
1191
|
-
|
|
1192
|
-
|
|
1193
|
-
|
|
1194
|
-
|
|
1195
|
-
|
|
1196
|
-
|
|
1197
|
-
|
|
1198
|
-
"
|
|
1199
|
-
|
|
1200
|
-
|
|
1201
|
-
|
|
1202
|
-
|
|
1203
|
-
|
|
1204
|
-
|
|
1267
|
+
),
|
|
1268
|
+
(inFlight.length > 0 || files.length > 0) && /* @__PURE__ */ jsxs4("ul", { className: "fillo-files", children: [
|
|
1269
|
+
files.map((f) => /* @__PURE__ */ jsxs4("li", { className: "fillo-file fillo-file--done", children: [
|
|
1270
|
+
/* @__PURE__ */ jsx4(FileStateIcon, { state: "done" }),
|
|
1271
|
+
/* @__PURE__ */ jsxs4("span", { className: "fillo-file-content", children: [
|
|
1272
|
+
/* @__PURE__ */ jsx4("span", { className: "fillo-file-name", children: f.name }),
|
|
1273
|
+
/* @__PURE__ */ jsx4("span", { className: "fillo-file-meta", children: strings.uploadedFile(formatBytes(f.size)) })
|
|
1274
|
+
] }),
|
|
1275
|
+
/* @__PURE__ */ jsx4("span", { className: "fillo-file-actions", children: /* @__PURE__ */ jsx4(
|
|
1276
|
+
"button",
|
|
1277
|
+
{
|
|
1278
|
+
type: "button",
|
|
1279
|
+
className: "fillo-file-remove",
|
|
1280
|
+
"aria-label": `${strings.uploadRemove} ${f.name}`,
|
|
1281
|
+
onClick: () => removeFile(f.fileId),
|
|
1282
|
+
children: /* @__PURE__ */ jsx4(CloseIcon, {})
|
|
1283
|
+
}
|
|
1284
|
+
) })
|
|
1285
|
+
] }, f.fileId)),
|
|
1286
|
+
inFlight.map((f) => /* @__PURE__ */ jsxs4("li", { className: `fillo-file${f.error ? " fillo-file--failed" : ""}`, children: [
|
|
1287
|
+
/* @__PURE__ */ jsx4(FileStateIcon, { state: f.error ? "failed" : "uploading" }),
|
|
1205
1288
|
f.error ? /* @__PURE__ */ jsxs4(Fragment2, { children: [
|
|
1289
|
+
/* @__PURE__ */ jsxs4("span", { className: "fillo-file-content", children: [
|
|
1290
|
+
/* @__PURE__ */ jsx4("span", { className: "fillo-file-name", children: f.name }),
|
|
1291
|
+
/* @__PURE__ */ jsx4("span", { className: "fillo-file-error", role: "alert", children: f.error })
|
|
1292
|
+
] }),
|
|
1206
1293
|
/* @__PURE__ */ jsxs4("span", { className: "fillo-file-actions", children: [
|
|
1207
1294
|
f.file && !f.tooLarge && remaining > 0 && /* @__PURE__ */ jsx4(
|
|
1208
1295
|
"button",
|
|
@@ -1224,50 +1311,65 @@ function FileUploadField({ field, value, error, setValue, api, ids: providedIds
|
|
|
1224
1311
|
{
|
|
1225
1312
|
type: "button",
|
|
1226
1313
|
className: "fillo-file-remove",
|
|
1227
|
-
"aria-label":
|
|
1314
|
+
"aria-label": `${strings.uploadDismiss} ${f.name}`,
|
|
1228
1315
|
onClick: () => setInFlight((prev) => prev.filter((x) => x.key !== f.key)),
|
|
1229
|
-
children:
|
|
1316
|
+
children: /* @__PURE__ */ jsx4(CloseIcon, {})
|
|
1230
1317
|
}
|
|
1231
1318
|
)
|
|
1232
|
-
] })
|
|
1233
|
-
/* @__PURE__ */ jsx4("span", { className: "fillo-file-error", role: "alert", children: f.error })
|
|
1319
|
+
] })
|
|
1234
1320
|
] }) : /* @__PURE__ */ jsxs4(Fragment2, { children: [
|
|
1235
|
-
/* @__PURE__ */ jsxs4("span", { className: "fillo-file-
|
|
1236
|
-
|
|
1237
|
-
"
|
|
1238
|
-
formatBytes(f.size)
|
|
1321
|
+
/* @__PURE__ */ jsxs4("span", { className: "fillo-file-content", children: [
|
|
1322
|
+
/* @__PURE__ */ jsx4("span", { className: "fillo-file-name", children: f.name }),
|
|
1323
|
+
/* @__PURE__ */ jsx4("span", { className: "fillo-file-meta", children: strings.uploadingFile(Math.round(f.fraction * 100), formatBytes(f.size)) })
|
|
1239
1324
|
] }),
|
|
1240
|
-
/* @__PURE__ */ jsx4("span", { className: "fillo-
|
|
1241
|
-
/* @__PURE__ */ jsx4(
|
|
1325
|
+
/* @__PURE__ */ jsx4("span", { className: "fillo-file-actions", children: /* @__PURE__ */ jsx4(
|
|
1242
1326
|
"button",
|
|
1243
1327
|
{
|
|
1244
1328
|
type: "button",
|
|
1245
1329
|
className: "fillo-file-remove",
|
|
1246
|
-
"aria-label":
|
|
1330
|
+
"aria-label": `${strings.uploadCancel} ${f.name}`,
|
|
1247
1331
|
onClick: () => cancelUpload(f.key),
|
|
1248
|
-
children:
|
|
1332
|
+
children: /* @__PURE__ */ jsx4(CloseIcon, {})
|
|
1333
|
+
}
|
|
1334
|
+
) }),
|
|
1335
|
+
/* @__PURE__ */ jsx4(
|
|
1336
|
+
"span",
|
|
1337
|
+
{
|
|
1338
|
+
className: "fillo-progress",
|
|
1339
|
+
role: "progressbar",
|
|
1340
|
+
"aria-label": `Uploading ${f.name}`,
|
|
1341
|
+
"aria-valuemin": 0,
|
|
1342
|
+
"aria-valuemax": 100,
|
|
1343
|
+
"aria-valuenow": Math.round(f.fraction * 100),
|
|
1344
|
+
children: /* @__PURE__ */ jsx4(
|
|
1345
|
+
"span",
|
|
1346
|
+
{
|
|
1347
|
+
className: "fillo-progress-bar",
|
|
1348
|
+
style: { width: `${f.fraction * 100}%` }
|
|
1349
|
+
}
|
|
1350
|
+
)
|
|
1249
1351
|
}
|
|
1250
1352
|
)
|
|
1251
1353
|
] })
|
|
1252
|
-
]
|
|
1253
|
-
},
|
|
1254
|
-
|
|
1255
|
-
|
|
1256
|
-
|
|
1257
|
-
|
|
1258
|
-
] });
|
|
1354
|
+
] }, f.key))
|
|
1355
|
+
] }),
|
|
1356
|
+
error && /* @__PURE__ */ jsx4("p", { className: slots.error, "data-fillo": "error", id: ids.errorId, children: error })
|
|
1357
|
+
]
|
|
1358
|
+
}
|
|
1359
|
+
);
|
|
1259
1360
|
}
|
|
1260
1361
|
|
|
1261
1362
|
// src/fields.tsx
|
|
1262
1363
|
import { jsx as jsx5, jsxs as jsxs5 } from "react/jsx-runtime";
|
|
1263
|
-
function fieldAria(field, error, ids) {
|
|
1364
|
+
function fieldAria(field, error, ids, opts = {}) {
|
|
1264
1365
|
const describedBy = [
|
|
1265
1366
|
field.description ? ids.descriptionId : null,
|
|
1266
1367
|
error ? ids.errorId : null
|
|
1267
1368
|
].filter(Boolean);
|
|
1369
|
+
const requiredSupported = opts.required ?? true;
|
|
1268
1370
|
return {
|
|
1269
1371
|
"aria-invalid": error ? true : void 0,
|
|
1270
|
-
"aria-required": field.required ? true : void 0,
|
|
1372
|
+
"aria-required": requiredSupported && field.required ? true : void 0,
|
|
1271
1373
|
"aria-describedby": describedBy.length ? describedBy.join(" ") : void 0
|
|
1272
1374
|
};
|
|
1273
1375
|
}
|
|
@@ -1360,7 +1462,8 @@ function NumberInput({ field, value, error, setValue, api, ids: providedIds }) {
|
|
|
1360
1462
|
inputRef.current?.setSelectionRange(pos, pos);
|
|
1361
1463
|
});
|
|
1362
1464
|
const num = field;
|
|
1363
|
-
const grouped = num.notation
|
|
1465
|
+
const grouped = num.notation !== void 0;
|
|
1466
|
+
const locale = localeForNotation(num.notation);
|
|
1364
1467
|
const hasAffix = Boolean(num.prefix) || Boolean(num.suffix);
|
|
1365
1468
|
if (!grouped && !hasAffix) {
|
|
1366
1469
|
return /* @__PURE__ */ jsx5(
|
|
@@ -1377,7 +1480,7 @@ function NumberInput({ field, value, error, setValue, api, ids: providedIds }) {
|
|
|
1377
1480
|
}
|
|
1378
1481
|
const stored = typeof value === "string" || typeof value === "number" ? String(value) : "";
|
|
1379
1482
|
const n = Number(stored);
|
|
1380
|
-
const display = draft !== null ? draft : grouped && stored !== "" && Number.isFinite(n) ? formatGroupedNumber(n, { decimals: num.decimals }) : stored;
|
|
1483
|
+
const display = draft !== null ? draft : grouped && stored !== "" && Number.isFinite(n) ? formatGroupedNumber(n, { locale, decimals: num.decimals }) : stored;
|
|
1381
1484
|
const input = /* @__PURE__ */ jsx5(
|
|
1382
1485
|
"input",
|
|
1383
1486
|
{
|
|
@@ -1390,14 +1493,14 @@ function NumberInput({ field, value, error, setValue, api, ids: providedIds }) {
|
|
|
1390
1493
|
placeholder: field.placeholder,
|
|
1391
1494
|
onBlur: grouped ? () => setDraft(null) : void 0,
|
|
1392
1495
|
onChange: (e) => {
|
|
1393
|
-
if (!isValidPartialNumberText(e.target.value)) {
|
|
1496
|
+
if (!isValidPartialNumberText(e.target.value, locale)) {
|
|
1394
1497
|
pendingCaret.current = e.target.selectionStart ?? e.target.value.length;
|
|
1395
1498
|
setRevertTick((t) => t + 1);
|
|
1396
1499
|
return;
|
|
1397
1500
|
}
|
|
1398
1501
|
if (grouped) {
|
|
1399
1502
|
setDraft(e.target.value);
|
|
1400
|
-
setValue(parseGroupedNumber(e.target.value));
|
|
1503
|
+
setValue(parseGroupedNumber(e.target.value, locale));
|
|
1401
1504
|
} else {
|
|
1402
1505
|
setValue(e.target.value);
|
|
1403
1506
|
}
|
|
@@ -1590,7 +1693,7 @@ function MultiChoice({ field, value, error, setValue, ids: providedIds }) {
|
|
|
1590
1693
|
"data-fillo": "options",
|
|
1591
1694
|
role: "group",
|
|
1592
1695
|
"aria-labelledby": ids.labelId,
|
|
1593
|
-
...fieldAria(field, error, ids),
|
|
1696
|
+
...fieldAria(field, error, ids, { required: false }),
|
|
1594
1697
|
children: [
|
|
1595
1698
|
options.map((opt) => /* @__PURE__ */ jsxs5(
|
|
1596
1699
|
"label",
|
|
@@ -1967,7 +2070,7 @@ function Ranking({ field, value, error, setValue, ids: providedIds }) {
|
|
|
1967
2070
|
id: ids.inputId,
|
|
1968
2071
|
role: "group",
|
|
1969
2072
|
"aria-labelledby": ids.labelId,
|
|
1970
|
-
...fieldAria(field, error, ids),
|
|
2073
|
+
...fieldAria(field, error, ids, { required: false }),
|
|
1971
2074
|
children: /* @__PURE__ */ jsx5("ol", { className: "fillo-ranking", children: order.map((id, index) => {
|
|
1972
2075
|
const option = ranking.options.find((o) => o.id === id);
|
|
1973
2076
|
if (!option) return null;
|
|
@@ -2014,10 +2117,10 @@ function Matrix({ field, value, error, setValue, ids: providedIds }) {
|
|
|
2014
2117
|
className: "fillo-matrix-wrap",
|
|
2015
2118
|
role: "group",
|
|
2016
2119
|
"aria-labelledby": ids.labelId,
|
|
2017
|
-
...fieldAria(field, error, ids),
|
|
2120
|
+
...fieldAria(field, error, ids, { required: false }),
|
|
2018
2121
|
children: /* @__PURE__ */ jsxs5("table", { className: "fillo-matrix", children: [
|
|
2019
2122
|
/* @__PURE__ */ jsx5("thead", { children: /* @__PURE__ */ jsxs5("tr", { children: [
|
|
2020
|
-
/* @__PURE__ */ jsx5("
|
|
2123
|
+
/* @__PURE__ */ jsx5("td", {}),
|
|
2021
2124
|
matrix.columns.map((col) => /* @__PURE__ */ jsx5("th", { scope: "col", children: col.label }, col.id))
|
|
2022
2125
|
] }) }),
|
|
2023
2126
|
/* @__PURE__ */ jsx5("tbody", { children: matrix.rows.map((row) => {
|
|
@@ -2182,7 +2285,7 @@ function BlockRendererInner({ block, api, components, customComponents }) {
|
|
|
2182
2285
|
Component = components?.[block.kind] ?? DEFAULT_COMPONENTS[block.kind];
|
|
2183
2286
|
}
|
|
2184
2287
|
const rawError = api.errors[block.id];
|
|
2185
|
-
const error = rawError === REQUIRED_FIELD_MESSAGE ? strings
|
|
2288
|
+
const error = rawError === REQUIRED_FIELD_MESSAGE ? requiredFieldMessage(block, strings) : rawError;
|
|
2186
2289
|
return /* @__PURE__ */ jsx5(
|
|
2187
2290
|
Component,
|
|
2188
2291
|
{
|
|
@@ -2382,16 +2485,13 @@ function nextFrame(run) {
|
|
|
2382
2485
|
if (typeof requestAnimationFrame === "function") requestAnimationFrame(run);
|
|
2383
2486
|
else setTimeout(run, 0);
|
|
2384
2487
|
}
|
|
2385
|
-
function
|
|
2488
|
+
function focusFirstInvalid(formEl) {
|
|
2386
2489
|
if (!formEl || typeof window === "undefined") return;
|
|
2387
2490
|
nextFrame(() => {
|
|
2388
|
-
formEl.querySelector(
|
|
2491
|
+
const invalid = formEl.querySelector('[aria-invalid="true"]');
|
|
2492
|
+
resolveFocusable(invalid)?.focus();
|
|
2389
2493
|
});
|
|
2390
2494
|
}
|
|
2391
|
-
function focusField(inputId) {
|
|
2392
|
-
if (typeof document === "undefined") return;
|
|
2393
|
-
resolveFocusable(document.getElementById(inputId))?.focus();
|
|
2394
|
-
}
|
|
2395
2495
|
var HP_STYLE = {
|
|
2396
2496
|
position: "absolute",
|
|
2397
2497
|
left: "-9999px",
|
|
@@ -2410,12 +2510,15 @@ function assertHpHidden(el) {
|
|
|
2410
2510
|
}
|
|
2411
2511
|
function FilloForm(props) {
|
|
2412
2512
|
const { form, formId } = props;
|
|
2513
|
+
const renderOnly = props.renderOnly === true || props.skipValidation === true;
|
|
2413
2514
|
const client = useMemo5(
|
|
2414
|
-
() => props.client ?? (formId ? createClient() : void 0),
|
|
2415
|
-
[props.client, formId]
|
|
2515
|
+
() => renderOnly ? void 0 : props.client ?? (formId ? createClient() : void 0),
|
|
2516
|
+
[renderOnly, props.client, formId]
|
|
2416
2517
|
);
|
|
2417
2518
|
const [loaded, setLoaded] = useState6(null);
|
|
2418
|
-
const [loadFailure, setLoadFailure] = useState6(
|
|
2519
|
+
const [loadFailure, setLoadFailure] = useState6(
|
|
2520
|
+
null
|
|
2521
|
+
);
|
|
2419
2522
|
const devEnv = useIsDevEnv();
|
|
2420
2523
|
const devChrome = props.preview === true || devEnv;
|
|
2421
2524
|
if (props.preview === true) warnPreviewInProduction();
|
|
@@ -2449,6 +2552,18 @@ function FilloForm(props) {
|
|
|
2449
2552
|
useEffect8(() => {
|
|
2450
2553
|
onErrorRef.current = onError;
|
|
2451
2554
|
});
|
|
2555
|
+
const targetError = useMemo5(
|
|
2556
|
+
() => form && !formId && !renderOnly && (!isCodeForm2(form) || !props.client) ? new FilloError2(
|
|
2557
|
+
"This form has no Fillo target. Pass formId with the schema, use a defineForm() value with client, or set renderOnly for a non-submitting preview.",
|
|
2558
|
+
422,
|
|
2559
|
+
void 0,
|
|
2560
|
+
"form_target_required"
|
|
2561
|
+
) : null,
|
|
2562
|
+
[form, formId, props.client, renderOnly]
|
|
2563
|
+
);
|
|
2564
|
+
useEffect8(() => {
|
|
2565
|
+
if (targetError) onErrorRef.current?.(targetError);
|
|
2566
|
+
}, [targetError]);
|
|
2452
2567
|
const needsFetch = !inlineSchema && Boolean(client && formId);
|
|
2453
2568
|
useEffect8(() => {
|
|
2454
2569
|
if (!needsFetch || !client || !formId) {
|
|
@@ -2487,10 +2602,28 @@ function FilloForm(props) {
|
|
|
2487
2602
|
const currentLoaded = needsFetch && loaded?.formId === formId ? loaded : null;
|
|
2488
2603
|
const loadError = needsFetch && loadFailure && loadFailure.formId === formId ? loadFailure.error : null;
|
|
2489
2604
|
const rawSchema = inlineSchema ?? currentLoaded?.schema ?? null;
|
|
2490
|
-
const normalized = useMemo5(
|
|
2605
|
+
const normalized = useMemo5(
|
|
2606
|
+
() => rawSchema ? normalizeFormSchema(rawSchema) : null,
|
|
2607
|
+
[rawSchema]
|
|
2608
|
+
);
|
|
2491
2609
|
const schema = normalized?.ok ? normalized.schema : null;
|
|
2492
2610
|
const resolvedCodeTheme = renderResolvedSnapshot ? resolvedTheme ?? void 0 : codeForm?.theme;
|
|
2493
2611
|
const theme = props.appearance?.theme ?? props.theme ?? resolvedCodeTheme ?? currentLoaded?.theme ?? void 0;
|
|
2612
|
+
if (targetError) {
|
|
2613
|
+
if (props.renderError) return /* @__PURE__ */ jsx7(Fragment3, { children: props.renderError(targetError) });
|
|
2614
|
+
return /* @__PURE__ */ jsx7(
|
|
2615
|
+
"div",
|
|
2616
|
+
{
|
|
2617
|
+
className: `fillo-form fillo-form--error ${props.className ?? ""}`,
|
|
2618
|
+
role: "alert",
|
|
2619
|
+
"data-fillo": "root",
|
|
2620
|
+
"data-state": "error",
|
|
2621
|
+
"data-error-code": "form_target_required",
|
|
2622
|
+
...themeProps(theme),
|
|
2623
|
+
children: devChrome ? `${targetError.message} (${targetError.code})` : strings.loadFailed
|
|
2624
|
+
}
|
|
2625
|
+
);
|
|
2626
|
+
}
|
|
2494
2627
|
if (normalized && !normalized.ok) {
|
|
2495
2628
|
const error = new FilloError2(`This form could not be rendered: ${normalized.error}`, 422);
|
|
2496
2629
|
if (props.renderError) return /* @__PURE__ */ jsx7(Fragment3, { children: props.renderError(error) });
|
|
@@ -2531,7 +2664,14 @@ function FilloForm(props) {
|
|
|
2531
2664
|
);
|
|
2532
2665
|
}
|
|
2533
2666
|
if (currentLoaded?.closed) {
|
|
2534
|
-
return /* @__PURE__ */ jsx7(
|
|
2667
|
+
return /* @__PURE__ */ jsx7(
|
|
2668
|
+
"div",
|
|
2669
|
+
{
|
|
2670
|
+
className: `fillo-form fillo-form--closed ${props.className ?? ""}`,
|
|
2671
|
+
...themeProps(theme),
|
|
2672
|
+
children: /* @__PURE__ */ jsx7("p", { className: "fillo-closed", children: strings.closed })
|
|
2673
|
+
}
|
|
2674
|
+
);
|
|
2535
2675
|
}
|
|
2536
2676
|
const canonicalSyncError = codeForm && syncError && !props.skipValidation ? syncError : null;
|
|
2537
2677
|
if (canonicalSyncError && props.renderError) {
|
|
@@ -2573,7 +2713,14 @@ function FilloForm(props) {
|
|
|
2573
2713
|
}
|
|
2574
2714
|
);
|
|
2575
2715
|
}
|
|
2576
|
-
return /* @__PURE__ */ jsx7(
|
|
2716
|
+
return /* @__PURE__ */ jsx7(
|
|
2717
|
+
"div",
|
|
2718
|
+
{
|
|
2719
|
+
className: `fillo-form fillo-form--closed ${props.className ?? ""}`,
|
|
2720
|
+
...themeProps(theme),
|
|
2721
|
+
children: /* @__PURE__ */ jsx7("p", { className: "fillo-closed", children: variant === "closed" ? strings.closed : strings.notLive })
|
|
2722
|
+
}
|
|
2723
|
+
);
|
|
2577
2724
|
}
|
|
2578
2725
|
const awaitingCanonical = Boolean(codeForm && codeFormSyncing && !props.skipValidation);
|
|
2579
2726
|
if (!devChrome && awaitingCanonical || !schema) {
|
|
@@ -2598,10 +2745,12 @@ function FilloForm(props) {
|
|
|
2598
2745
|
ResolvedForm,
|
|
2599
2746
|
{
|
|
2600
2747
|
...props,
|
|
2601
|
-
client,
|
|
2748
|
+
client: renderOnly ? void 0 : client,
|
|
2602
2749
|
schema,
|
|
2603
2750
|
theme,
|
|
2604
|
-
formId: syncedFormId ?? formId,
|
|
2751
|
+
formId: renderOnly ? void 0 : syncedFormId ?? formId,
|
|
2752
|
+
renderOnly,
|
|
2753
|
+
skipValidation: renderOnly ? true : props.skipValidation,
|
|
2605
2754
|
poweredBy,
|
|
2606
2755
|
brandLabel: branding?.label,
|
|
2607
2756
|
brandHref: branding?.href,
|
|
@@ -2672,13 +2821,14 @@ function useIsDevEnv() {
|
|
|
2672
2821
|
function ResolvedForm(props) {
|
|
2673
2822
|
const { schema, theme } = props;
|
|
2674
2823
|
const displayOnly = props.displayOnly === true;
|
|
2824
|
+
const renderOnly = props.renderOnly === true;
|
|
2675
2825
|
const instanceId = useId2().replace(/:/g, "");
|
|
2676
2826
|
const devEnv = useIsDevEnv();
|
|
2677
2827
|
const devChrome = !displayOnly && (props.preview === true || devEnv);
|
|
2678
2828
|
const hpRef = useRef8(null);
|
|
2679
2829
|
const codeForm = isCodeForm2(props.form) ? props.form : null;
|
|
2680
2830
|
const client = props.client;
|
|
2681
|
-
const challenge = props.challenge;
|
|
2831
|
+
const challenge = renderOnly ? void 0 : props.challenge;
|
|
2682
2832
|
const challengeRequired = Boolean(challenge);
|
|
2683
2833
|
const challengeTokenRef = useRef8(void 0);
|
|
2684
2834
|
const [resyncWarning, setResyncWarning] = useState6(null);
|
|
@@ -2790,7 +2940,7 @@ function ResolvedForm(props) {
|
|
|
2790
2940
|
resetChallengeRef.current?.();
|
|
2791
2941
|
}
|
|
2792
2942
|
});
|
|
2793
|
-
useDraftFlush(api, !displayOnly);
|
|
2943
|
+
useDraftFlush(api, !displayOnly && !renderOnly);
|
|
2794
2944
|
const strings = resolveStrings(props.strings);
|
|
2795
2945
|
const announceRef = useRef8(null);
|
|
2796
2946
|
const announce = useCallback((text) => {
|
|
@@ -2821,12 +2971,13 @@ function ResolvedForm(props) {
|
|
|
2821
2971
|
const chromeValue = useMemo5(
|
|
2822
2972
|
() => ({
|
|
2823
2973
|
devChrome,
|
|
2974
|
+
renderOnly,
|
|
2824
2975
|
uploadsAvailable,
|
|
2825
2976
|
uploadFileSizeLimitMb,
|
|
2826
2977
|
warningCode,
|
|
2827
2978
|
warningUrl
|
|
2828
2979
|
}),
|
|
2829
|
-
[devChrome, uploadsAvailable, uploadFileSizeLimitMb, warningCode, warningUrl]
|
|
2980
|
+
[devChrome, renderOnly, uploadsAvailable, uploadFileSizeLimitMb, warningCode, warningUrl]
|
|
2830
2981
|
);
|
|
2831
2982
|
const settings = schema.settings;
|
|
2832
2983
|
const submitted = api.status === "submitted";
|
|
@@ -2867,13 +3018,17 @@ function ResolvedForm(props) {
|
|
|
2867
3018
|
{ slot: "root", status: "submitted" }
|
|
2868
3019
|
),
|
|
2869
3020
|
"data-fillo": "root",
|
|
3021
|
+
"data-fillo-form-id": props.formId,
|
|
2870
3022
|
"data-state": "submitted",
|
|
2871
3023
|
...themed,
|
|
2872
3024
|
children: [
|
|
2873
3025
|
props.renderSuccess ? props.renderSuccess() : /* @__PURE__ */ jsxs6(
|
|
2874
3026
|
"div",
|
|
2875
3027
|
{
|
|
2876
|
-
className: slotClass3("fillo-success", appearance, {
|
|
3028
|
+
className: slotClass3("fillo-success", appearance, {
|
|
3029
|
+
slot: "success",
|
|
3030
|
+
status: "submitted"
|
|
3031
|
+
}),
|
|
2877
3032
|
"data-fillo": "success",
|
|
2878
3033
|
children: [
|
|
2879
3034
|
/* @__PURE__ */ jsx7("div", { className: "fillo-success-mark", "aria-hidden": "true" }),
|
|
@@ -2899,17 +3054,6 @@ function ResolvedForm(props) {
|
|
|
2899
3054
|
const progressPos = progressSeq.indexOf(api.page.id);
|
|
2900
3055
|
const progressStep = progressPos >= 0 ? progressPos + 1 : Math.min(api.pageIndex + 1, progressTotal);
|
|
2901
3056
|
const submitFixUrl = devChrome && resolutionFailureCode === "form_not_published" && warningCode === "storage_required" ? safeHttpUrl(warningUrl) : null;
|
|
2902
|
-
const invalidFields = [];
|
|
2903
|
-
for (const block of api.blocks) {
|
|
2904
|
-
if (!isField2(block)) continue;
|
|
2905
|
-
const raw = api.errors[block.id];
|
|
2906
|
-
if (!raw) continue;
|
|
2907
|
-
invalidFields.push({
|
|
2908
|
-
field: block,
|
|
2909
|
-
error: raw === REQUIRED_FIELD_MESSAGE2 ? strings.required : raw,
|
|
2910
|
-
inputId: createFilloFieldIds(instanceId, block.id).inputId
|
|
2911
|
-
});
|
|
2912
|
-
}
|
|
2913
3057
|
const rootClass = slotClass3(`fillo-form ${props.className ?? ""}`, appearance, {
|
|
2914
3058
|
slot: "root",
|
|
2915
3059
|
status: api.status
|
|
@@ -2934,7 +3078,7 @@ function ResolvedForm(props) {
|
|
|
2934
3078
|
staged: props.stagedNotice,
|
|
2935
3079
|
draft: props.draftNotice,
|
|
2936
3080
|
warningUrl: props.syncWarningUrl,
|
|
2937
|
-
noClient: !props.client && !props.skipValidation
|
|
3081
|
+
noClient: !props.client && !props.skipValidation && !renderOnly
|
|
2938
3082
|
}
|
|
2939
3083
|
),
|
|
2940
3084
|
resuming && /* @__PURE__ */ jsxs6(
|
|
@@ -2951,7 +3095,9 @@ function ResolvedForm(props) {
|
|
|
2951
3095
|
api.resumeLinkFailed && !api.resumedDraft && !api.editingPrevious && /* @__PURE__ */ jsx7(
|
|
2952
3096
|
"div",
|
|
2953
3097
|
{
|
|
2954
|
-
className: slotClass3("fillo-resume fillo-resume--expired", appearance, {
|
|
3098
|
+
className: slotClass3("fillo-resume fillo-resume--expired", appearance, {
|
|
3099
|
+
slot: "resume"
|
|
3100
|
+
}),
|
|
2955
3101
|
"data-fillo": "resume",
|
|
2956
3102
|
role: "status",
|
|
2957
3103
|
children: /* @__PURE__ */ jsx7("span", { className: "fillo-resume-text", children: strings.resumeLinkExpired })
|
|
@@ -2972,51 +3118,65 @@ function ResolvedForm(props) {
|
|
|
2972
3118
|
{
|
|
2973
3119
|
className: slotClass3("fillo-progress-fill", appearance, { slot: "progressFill" }),
|
|
2974
3120
|
"data-fillo": "progressFill",
|
|
2975
|
-
style: {
|
|
3121
|
+
style: {
|
|
3122
|
+
"--fillo-progress-value": `${progressStep / progressTotal * 100}%`
|
|
3123
|
+
}
|
|
2976
3124
|
}
|
|
2977
3125
|
)
|
|
2978
3126
|
}
|
|
2979
3127
|
),
|
|
2980
|
-
api.pageIndex === 0 && props.showTitle !== false && /* @__PURE__ */ jsxs6(
|
|
2981
|
-
|
|
2982
|
-
schema.description && /* @__PURE__ */ jsx7(
|
|
2983
|
-
"p",
|
|
2984
|
-
{
|
|
2985
|
-
className: slotClass3("fillo-form-description", appearance, { slot: "description" }),
|
|
2986
|
-
"data-fillo": "description",
|
|
2987
|
-
children: schema.description
|
|
2988
|
-
}
|
|
2989
|
-
)
|
|
2990
|
-
] }),
|
|
2991
|
-
multiPage && api.page.title && api.pageIndex > 0 && /* @__PURE__ */ jsx7("h2", { className: slotClass3("fillo-page-title", appearance, { slot: "pageTitle" }), "data-fillo": "pageTitle", children: api.page.title }),
|
|
2992
|
-
invalidFields.length > 0 && /* @__PURE__ */ jsxs6("div", { className: "fillo-error-summary", role: "alert", tabIndex: -1, "data-fillo": "error-summary", children: [
|
|
2993
|
-
/* @__PURE__ */ jsx7("p", { className: "fillo-error-summary-title", children: strings.errorSummaryTitle }),
|
|
2994
|
-
/* @__PURE__ */ jsx7("ul", { children: invalidFields.map(({ field, error, inputId }) => /* @__PURE__ */ jsx7("li", { children: /* @__PURE__ */ jsxs6(
|
|
2995
|
-
"a",
|
|
2996
|
-
{
|
|
2997
|
-
href: `#${inputId}`,
|
|
2998
|
-
onClick: (e) => {
|
|
2999
|
-
e.preventDefault();
|
|
3000
|
-
focusField(inputId);
|
|
3001
|
-
},
|
|
3002
|
-
children: [
|
|
3003
|
-
field.label,
|
|
3004
|
-
": ",
|
|
3005
|
-
error
|
|
3006
|
-
]
|
|
3007
|
-
}
|
|
3008
|
-
) }, field.id)) })
|
|
3009
|
-
] }),
|
|
3010
|
-
/* @__PURE__ */ jsx7("div", { className: slotClass3("fillo-blocks", appearance, { slot: "blocks" }), "data-fillo": "blocks", ref: blocksRef, tabIndex: -1, children: api.blocks.map((block) => /* @__PURE__ */ jsx7(
|
|
3011
|
-
BlockRenderer,
|
|
3128
|
+
api.pageIndex === 0 && props.showTitle !== false && /* @__PURE__ */ jsxs6(
|
|
3129
|
+
"header",
|
|
3012
3130
|
{
|
|
3013
|
-
|
|
3014
|
-
|
|
3015
|
-
|
|
3016
|
-
|
|
3017
|
-
|
|
3018
|
-
|
|
3019
|
-
|
|
3131
|
+
className: slotClass3("fillo-header", appearance, { slot: "header" }),
|
|
3132
|
+
"data-fillo": "header",
|
|
3133
|
+
children: [
|
|
3134
|
+
schema.title && /* @__PURE__ */ jsx7(
|
|
3135
|
+
"h1",
|
|
3136
|
+
{
|
|
3137
|
+
className: slotClass3("fillo-title", appearance, { slot: "title" }),
|
|
3138
|
+
"data-fillo": "title",
|
|
3139
|
+
children: schema.title
|
|
3140
|
+
}
|
|
3141
|
+
),
|
|
3142
|
+
schema.description && /* @__PURE__ */ jsx7(
|
|
3143
|
+
"p",
|
|
3144
|
+
{
|
|
3145
|
+
className: slotClass3("fillo-form-description", appearance, { slot: "description" }),
|
|
3146
|
+
"data-fillo": "description",
|
|
3147
|
+
children: schema.description
|
|
3148
|
+
}
|
|
3149
|
+
)
|
|
3150
|
+
]
|
|
3151
|
+
}
|
|
3152
|
+
),
|
|
3153
|
+
multiPage && api.page.title && api.pageIndex > 0 && /* @__PURE__ */ jsx7(
|
|
3154
|
+
"h2",
|
|
3155
|
+
{
|
|
3156
|
+
className: slotClass3("fillo-page-title", appearance, { slot: "pageTitle" }),
|
|
3157
|
+
"data-fillo": "pageTitle",
|
|
3158
|
+
children: api.page.title
|
|
3159
|
+
}
|
|
3160
|
+
),
|
|
3161
|
+
/* @__PURE__ */ jsx7(
|
|
3162
|
+
"div",
|
|
3163
|
+
{
|
|
3164
|
+
className: slotClass3("fillo-blocks", appearance, { slot: "blocks" }),
|
|
3165
|
+
"data-fillo": "blocks",
|
|
3166
|
+
ref: blocksRef,
|
|
3167
|
+
tabIndex: -1,
|
|
3168
|
+
children: api.blocks.map((block) => /* @__PURE__ */ jsx7(
|
|
3169
|
+
BlockRenderer,
|
|
3170
|
+
{
|
|
3171
|
+
block,
|
|
3172
|
+
api,
|
|
3173
|
+
components: props.components,
|
|
3174
|
+
customComponents: props.customComponents
|
|
3175
|
+
},
|
|
3176
|
+
block.id
|
|
3177
|
+
))
|
|
3178
|
+
}
|
|
3179
|
+
),
|
|
3020
3180
|
!displayOnly && /* @__PURE__ */ jsx7(
|
|
3021
3181
|
"input",
|
|
3022
3182
|
{
|
|
@@ -3056,43 +3216,66 @@ function ResolvedForm(props) {
|
|
|
3056
3216
|
/* @__PURE__ */ jsx7("a", { href: submitFixUrl, target: "_blank", rel: "noopener noreferrer", children: submitFixUrl })
|
|
3057
3217
|
] })
|
|
3058
3218
|
] }),
|
|
3059
|
-
showFooter && /* @__PURE__ */ jsxs6(
|
|
3060
|
-
|
|
3061
|
-
|
|
3062
|
-
{
|
|
3063
|
-
|
|
3064
|
-
|
|
3065
|
-
|
|
3066
|
-
|
|
3067
|
-
|
|
3068
|
-
|
|
3069
|
-
|
|
3070
|
-
|
|
3071
|
-
|
|
3072
|
-
|
|
3073
|
-
|
|
3074
|
-
|
|
3075
|
-
|
|
3076
|
-
|
|
3077
|
-
|
|
3078
|
-
|
|
3079
|
-
|
|
3080
|
-
|
|
3081
|
-
|
|
3082
|
-
|
|
3083
|
-
|
|
3219
|
+
showFooter && /* @__PURE__ */ jsxs6(
|
|
3220
|
+
"footer",
|
|
3221
|
+
{
|
|
3222
|
+
className: slotClass3("fillo-footer", appearance, { slot: "footer" }),
|
|
3223
|
+
"data-fillo": "footer",
|
|
3224
|
+
children: [
|
|
3225
|
+
multiPage && !api.isFirstPage && /* @__PURE__ */ jsx7(
|
|
3226
|
+
"button",
|
|
3227
|
+
{
|
|
3228
|
+
type: "button",
|
|
3229
|
+
className: slotClass3("fillo-button fillo-button--ghost", appearance, {
|
|
3230
|
+
slot: "button",
|
|
3231
|
+
variant: "ghost"
|
|
3232
|
+
}),
|
|
3233
|
+
"data-fillo": "button",
|
|
3234
|
+
onClick: api.back,
|
|
3235
|
+
children: strings.back
|
|
3236
|
+
}
|
|
3237
|
+
),
|
|
3238
|
+
showPrimaryButton && /* @__PURE__ */ jsx7(
|
|
3239
|
+
"button",
|
|
3240
|
+
{
|
|
3241
|
+
type: "submit",
|
|
3242
|
+
className: slotClass3("fillo-button fillo-button--primary", appearance, {
|
|
3243
|
+
slot: "button",
|
|
3244
|
+
variant: "primary"
|
|
3245
|
+
}),
|
|
3246
|
+
"data-fillo": "button",
|
|
3247
|
+
disabled: api.status === "submitting" || api.uploading || // Wait for the human check before enabling submit (never
|
|
3248
|
+
// "Next"). The server rejects a tokenless submit regardless —
|
|
3249
|
+
// this is UX only.
|
|
3250
|
+
showChallenge && !challengeToken,
|
|
3251
|
+
children: api.uploading ? strings.uploading : api.status === "submitting" ? strings.submitting : api.isLastPage ? settings.submitLabel ?? strings.submit : strings.next
|
|
3252
|
+
}
|
|
3253
|
+
)
|
|
3254
|
+
]
|
|
3255
|
+
}
|
|
3256
|
+
),
|
|
3084
3257
|
props.poweredBy && /* @__PURE__ */ jsx7(PoweredBy, { label: props.brandLabel, href: props.brandHref })
|
|
3085
3258
|
] });
|
|
3086
3259
|
return /* @__PURE__ */ jsx7(FilloContext.Provider, { value: api, children: /* @__PURE__ */ jsx7(FilloChromeContext.Provider, { value: chromeValue, children: /* @__PURE__ */ jsx7(FilloAnnounceContext.Provider, { value: announce, children: /* @__PURE__ */ jsx7(FilloStringsContext.Provider, { value: strings, children: /* @__PURE__ */ jsx7(FilloAppearanceContext.Provider, { value: appearance, children: /* @__PURE__ */ jsx7(FilloInstanceIdContext.Provider, { value: instanceId, children: displayOnly ? (
|
|
3087
3260
|
// Display-only preview: a plain div (no form element to submit) with
|
|
3088
3261
|
// every control inside a natively disabled fieldset — unfocusable
|
|
3089
3262
|
// and unfillable even without the stylesheet or inert support.
|
|
3090
|
-
/* @__PURE__ */ jsx7(
|
|
3263
|
+
/* @__PURE__ */ jsx7(
|
|
3264
|
+
"div",
|
|
3265
|
+
{
|
|
3266
|
+
className: rootClass,
|
|
3267
|
+
"data-fillo": "preview-form",
|
|
3268
|
+
"data-state": "preview",
|
|
3269
|
+
...themed,
|
|
3270
|
+
children: /* @__PURE__ */ jsx7("fieldset", { disabled: true, className: "fillo-not-open-fields", children: formBody })
|
|
3271
|
+
}
|
|
3272
|
+
)
|
|
3091
3273
|
) : /* @__PURE__ */ jsx7(
|
|
3092
3274
|
"form",
|
|
3093
3275
|
{
|
|
3094
3276
|
className: rootClass,
|
|
3095
3277
|
"data-fillo": "root",
|
|
3278
|
+
"data-fillo-form-id": props.formId,
|
|
3096
3279
|
"data-state": api.status,
|
|
3097
3280
|
"data-page": api.pageIndex + 1,
|
|
3098
3281
|
"data-last-page": api.isLastPage ? "" : void 0,
|
|
@@ -3102,11 +3285,11 @@ function ResolvedForm(props) {
|
|
|
3102
3285
|
e.preventDefault();
|
|
3103
3286
|
const formEl = e.currentTarget;
|
|
3104
3287
|
if (api.isLastPage) {
|
|
3105
|
-
void api.submit().then(() =>
|
|
3288
|
+
void api.submit().then(() => focusFirstInvalid(formEl)).catch(() => {
|
|
3106
3289
|
});
|
|
3107
3290
|
} else {
|
|
3108
3291
|
api.next();
|
|
3109
|
-
|
|
3292
|
+
focusFirstInvalid(formEl);
|
|
3110
3293
|
}
|
|
3111
3294
|
},
|
|
3112
3295
|
children: formBody
|
|
@@ -3138,7 +3321,7 @@ function JsxForm(props) {
|
|
|
3138
3321
|
if (!memo2.current || memo2.current.json !== json) {
|
|
3139
3322
|
memo2.current = { json, form: compiled };
|
|
3140
3323
|
}
|
|
3141
|
-
return /* @__PURE__ */ jsx8(FilloForm, { form: memo2.current.form, client, ...rest });
|
|
3324
|
+
return /* @__PURE__ */ jsx8(FilloForm, { ...{ form: memo2.current.form, client, ...rest } });
|
|
3142
3325
|
}
|
|
3143
3326
|
function defineFormFromJsx(element) {
|
|
3144
3327
|
if (!element || element.type !== JsxForm) {
|
|
@@ -3183,9 +3366,10 @@ var Fillo = {
|
|
|
3183
3366
|
};
|
|
3184
3367
|
|
|
3185
3368
|
// src/provider.tsx
|
|
3186
|
-
import { useId as useId3, useMemo as useMemo6, useState as useState7 } from "react";
|
|
3369
|
+
import { useEffect as useEffect9, useId as useId3, useMemo as useMemo6, useState as useState7 } from "react";
|
|
3187
3370
|
import {
|
|
3188
3371
|
FilloError as FilloError3,
|
|
3372
|
+
createClient as createClient2,
|
|
3189
3373
|
formSchemasEqual as formSchemasEqual2,
|
|
3190
3374
|
isFilloError as isFilloError3,
|
|
3191
3375
|
normalizeFormSchema as normalizeFormSchema2,
|
|
@@ -3203,13 +3387,33 @@ function FilloProvider({
|
|
|
3203
3387
|
onError,
|
|
3204
3388
|
renderError,
|
|
3205
3389
|
preview,
|
|
3390
|
+
renderOnly,
|
|
3391
|
+
skipValidation,
|
|
3206
3392
|
...options
|
|
3207
3393
|
}) {
|
|
3394
|
+
const localPreview = renderOnly === true || skipValidation === true;
|
|
3395
|
+
const activeClient = useMemo6(
|
|
3396
|
+
() => localPreview ? void 0 : client ?? (formId ? createClient2() : void 0),
|
|
3397
|
+
[client, formId, localPreview]
|
|
3398
|
+
);
|
|
3399
|
+
const activeFormId = localPreview ? void 0 : formId;
|
|
3208
3400
|
const instanceId = useId3().replace(/:/g, "");
|
|
3209
3401
|
const devEnv = useIsDevEnv();
|
|
3210
3402
|
const devChrome = preview === true || devEnv;
|
|
3211
3403
|
if (preview === true) warnPreviewInProduction();
|
|
3212
3404
|
const codeForm = isCodeForm2(form) ? form : null;
|
|
3405
|
+
const targetError = useMemo6(
|
|
3406
|
+
() => !formId && !localPreview && (!codeForm || !client) ? new FilloError3(
|
|
3407
|
+
"This form has no Fillo target. Pass formId with the schema, use a defineForm() value with client, or set renderOnly for a non-submitting preview.",
|
|
3408
|
+
422,
|
|
3409
|
+
void 0,
|
|
3410
|
+
"form_target_required"
|
|
3411
|
+
) : null,
|
|
3412
|
+
[client, codeForm, formId, localPreview]
|
|
3413
|
+
);
|
|
3414
|
+
useEffect9(() => {
|
|
3415
|
+
if (targetError) onError?.(targetError);
|
|
3416
|
+
}, [onError, targetError]);
|
|
3213
3417
|
const {
|
|
3214
3418
|
formId: syncedFormId,
|
|
3215
3419
|
status: syncedStatus,
|
|
@@ -3222,8 +3426,8 @@ function FilloProvider({
|
|
|
3222
3426
|
uploadFileSizeLimitMb,
|
|
3223
3427
|
warningCode,
|
|
3224
3428
|
warningUrl
|
|
3225
|
-
} = useCodeFormSync(form,
|
|
3226
|
-
allowUnsynced: Boolean(!
|
|
3429
|
+
} = useCodeFormSync(form, activeClient, onError, {
|
|
3430
|
+
allowUnsynced: Boolean(!activeClient || activeFormId || localPreview)
|
|
3227
3431
|
});
|
|
3228
3432
|
const [resyncWarning, setResyncWarning] = useState7(null);
|
|
3229
3433
|
const rawSchema = codeForm ? resolvedSchema && !devChrome ? resolvedSchema : codeForm.schema : form;
|
|
@@ -3233,9 +3437,9 @@ function FilloProvider({
|
|
|
3233
3437
|
}
|
|
3234
3438
|
const schema = normalized.schema;
|
|
3235
3439
|
const resolveFormId = useMemo6(
|
|
3236
|
-
() => codeForm &&
|
|
3440
|
+
() => codeForm && activeClient?.key ? async () => {
|
|
3237
3441
|
try {
|
|
3238
|
-
const result = await syncCodeForm3(
|
|
3442
|
+
const result = await syncCodeForm3(activeClient, codeForm, { bypassCache: true });
|
|
3239
3443
|
setResyncWarning({
|
|
3240
3444
|
code: result.warningCode,
|
|
3241
3445
|
url: result.warningUrl,
|
|
@@ -3297,13 +3501,14 @@ function FilloProvider({
|
|
|
3297
3501
|
throw failure;
|
|
3298
3502
|
}
|
|
3299
3503
|
} : void 0,
|
|
3300
|
-
[
|
|
3504
|
+
[activeClient, codeForm, onError, schema]
|
|
3301
3505
|
);
|
|
3302
3506
|
const api = useFilloController({
|
|
3303
3507
|
...options,
|
|
3304
|
-
client,
|
|
3508
|
+
client: activeClient,
|
|
3305
3509
|
form: schema,
|
|
3306
|
-
formId: syncedFormId ??
|
|
3510
|
+
formId: syncedFormId ?? activeFormId ?? void 0,
|
|
3511
|
+
skipValidation: localPreview,
|
|
3307
3512
|
resolveFormId,
|
|
3308
3513
|
// Dev chrome (preview or dev env) surfaces the developer-grade resolution
|
|
3309
3514
|
// failure (message + code); an explicit option wins.
|
|
@@ -3312,7 +3517,7 @@ function FilloProvider({
|
|
|
3312
3517
|
// method; recorded per response so usage stays measurable.
|
|
3313
3518
|
surface: "headless"
|
|
3314
3519
|
});
|
|
3315
|
-
useDraftFlush(api);
|
|
3520
|
+
useDraftFlush(api, !localPreview);
|
|
3316
3521
|
const freshWarningCode = resyncWarning ? resyncWarning.code : warningCode;
|
|
3317
3522
|
const freshWarningUrl = resyncWarning ? resyncWarning.url : warningUrl;
|
|
3318
3523
|
const freshUploadsAvailable = resyncWarning ? resyncWarning.uploadsAvailable : uploadsAvailable;
|
|
@@ -3320,20 +3525,32 @@ function FilloProvider({
|
|
|
3320
3525
|
const chromeValue = useMemo6(
|
|
3321
3526
|
() => ({
|
|
3322
3527
|
devChrome,
|
|
3528
|
+
renderOnly: localPreview,
|
|
3323
3529
|
uploadsAvailable: freshUploadsAvailable,
|
|
3324
3530
|
uploadFileSizeLimitMb: freshUploadFileSizeLimitMb,
|
|
3325
3531
|
warningCode: freshWarningCode,
|
|
3326
3532
|
warningUrl: freshWarningUrl
|
|
3327
3533
|
}),
|
|
3328
|
-
[
|
|
3534
|
+
[
|
|
3535
|
+
devChrome,
|
|
3536
|
+
localPreview,
|
|
3537
|
+
freshUploadsAvailable,
|
|
3538
|
+
freshUploadFileSizeLimitMb,
|
|
3539
|
+
freshWarningCode,
|
|
3540
|
+
freshWarningUrl
|
|
3541
|
+
]
|
|
3329
3542
|
);
|
|
3330
3543
|
const resolvedStrings = resolveStrings2(strings);
|
|
3331
|
-
|
|
3544
|
+
if (targetError) {
|
|
3545
|
+
if (renderError) return /* @__PURE__ */ jsx9(Fragment4, { children: renderError(targetError) });
|
|
3546
|
+
throw targetError;
|
|
3547
|
+
}
|
|
3548
|
+
const canonicalSyncError = syncError && !localPreview ? syncError : null;
|
|
3332
3549
|
if (canonicalSyncError && renderError) return /* @__PURE__ */ jsx9(Fragment4, { children: renderError(canonicalSyncError) });
|
|
3333
3550
|
if (canonicalSyncError && !devChrome) return null;
|
|
3334
|
-
if (codeFormSyncing && !
|
|
3335
|
-
const notPublished = Boolean(codeForm) && syncedStatus === "draft" && !
|
|
3336
|
-
const notAccepting = Boolean(codeForm) && syncedStatus === "published" && syncedAccepting === false && !
|
|
3551
|
+
if (codeFormSyncing && !localPreview && !devChrome) return null;
|
|
3552
|
+
const notPublished = Boolean(codeForm) && syncedStatus === "draft" && !localPreview;
|
|
3553
|
+
const notAccepting = Boolean(codeForm) && syncedStatus === "published" && syncedAccepting === false && !localPreview;
|
|
3337
3554
|
if ((notPublished || notAccepting) && !devChrome) {
|
|
3338
3555
|
const error = notPublished ? new FilloError3("This form isn't published yet.", 403, void 0, "form_not_published") : new FilloError3(
|
|
3339
3556
|
"This form is no longer accepting responses.",
|
|
@@ -3348,7 +3565,7 @@ function FilloProvider({
|
|
|
3348
3565
|
|
|
3349
3566
|
// src/index.ts
|
|
3350
3567
|
import {
|
|
3351
|
-
createClient as
|
|
3568
|
+
createClient as createClient3,
|
|
3352
3569
|
provisionWorkspace,
|
|
3353
3570
|
when,
|
|
3354
3571
|
FilloJsxError as FilloJsxError2,
|
|
@@ -3375,7 +3592,7 @@ export {
|
|
|
3375
3592
|
FilloJsxError2 as FilloJsxError,
|
|
3376
3593
|
FilloProvider,
|
|
3377
3594
|
FormField,
|
|
3378
|
-
|
|
3595
|
+
createClient3 as createClient,
|
|
3379
3596
|
defineForm,
|
|
3380
3597
|
provisionWorkspace,
|
|
3381
3598
|
useField,
|