@page-speed/forms 0.5.1 → 0.5.3
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 +29 -0
- package/dist/chunk-232KNGJI.js +207 -0
- package/dist/chunk-232KNGJI.js.map +1 -0
- package/dist/chunk-24RPM43T.js +373 -0
- package/dist/chunk-24RPM43T.js.map +1 -0
- package/dist/chunk-27JUYRDE.cjs +173 -0
- package/dist/chunk-27JUYRDE.cjs.map +1 -0
- package/dist/chunk-5NT5T5XY.js +4136 -0
- package/dist/chunk-5NT5T5XY.js.map +1 -0
- package/dist/chunk-AVAKC6R7.cjs +236 -0
- package/dist/chunk-AVAKC6R7.cjs.map +1 -0
- package/dist/chunk-DKLLPKZN.cjs +238 -0
- package/dist/chunk-DKLLPKZN.cjs.map +1 -0
- package/dist/chunk-EX6CRLKG.cjs +397 -0
- package/dist/chunk-EX6CRLKG.cjs.map +1 -0
- package/dist/chunk-H6NNFV64.js +127 -0
- package/dist/chunk-H6NNFV64.js.map +1 -0
- package/dist/chunk-JBEWTBFH.js +217 -0
- package/dist/chunk-JBEWTBFH.js.map +1 -0
- package/dist/chunk-JBEZLX3H.cjs +138 -0
- package/dist/chunk-JBEZLX3H.cjs.map +1 -0
- package/dist/chunk-VLGZG2VP.js +150 -0
- package/dist/chunk-VLGZG2VP.js.map +1 -0
- package/dist/chunk-ZYFTT6DB.cjs +4169 -0
- package/dist/chunk-ZYFTT6DB.cjs.map +1 -0
- package/dist/core.cjs +23 -722
- package/dist/core.cjs.map +1 -1
- package/dist/core.d.cts +3 -3
- package/dist/core.d.ts +3 -3
- package/dist/core.js +3 -705
- package/dist/core.js.map +1 -1
- package/dist/index.cjs +43 -727
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.cts +1 -1
- package/dist/index.d.ts +1 -1
- package/dist/index.js +3 -705
- package/dist/index.js.map +1 -1
- package/dist/inputs.cjs +44 -4359
- package/dist/inputs.cjs.map +1 -1
- package/dist/inputs.d.cts +1 -1
- package/dist/inputs.d.ts +1 -1
- package/dist/inputs.js +2 -4337
- package/dist/inputs.js.map +1 -1
- package/dist/integration.cjs +574 -9
- package/dist/integration.cjs.map +1 -1
- package/dist/integration.d.cts +298 -1
- package/dist/integration.d.ts +298 -1
- package/dist/integration.js +566 -9
- package/dist/integration.js.map +1 -1
- package/dist/{types-DuX3q6A4.d.cts → types-CnOCn7b3.d.cts} +51 -1
- package/dist/{types-DuX3q6A4.d.ts → types-CnOCn7b3.d.ts} +51 -1
- package/dist/validation-rules.cjs +75 -231
- package/dist/validation-rules.cjs.map +1 -1
- package/dist/validation-rules.d.cts +1 -1
- package/dist/validation-rules.d.ts +1 -1
- package/dist/validation-rules.js +1 -215
- package/dist/validation-rules.js.map +1 -1
- package/dist/validation-utils.cjs +43 -133
- package/dist/validation-utils.cjs.map +1 -1
- package/dist/validation-utils.d.cts +1 -1
- package/dist/validation-utils.d.ts +1 -1
- package/dist/validation-utils.js +1 -125
- package/dist/validation-utils.js.map +1 -1
- package/dist/validation-valibot.d.cts +1 -1
- package/dist/validation-valibot.d.ts +1 -1
- package/dist/validation.cjs +115 -364
- package/dist/validation.cjs.map +1 -1
- package/dist/validation.d.cts +1 -1
- package/dist/validation.d.ts +1 -1
- package/dist/validation.js +2 -339
- package/dist/validation.js.map +1 -1
- package/package.json +1 -1
package/dist/integration.cjs
CHANGED
|
@@ -1,6 +1,9 @@
|
|
|
1
1
|
'use strict';
|
|
2
2
|
|
|
3
|
-
var
|
|
3
|
+
var chunkEX6CRLKG_cjs = require('./chunk-EX6CRLKG.cjs');
|
|
4
|
+
var chunkZYFTT6DB_cjs = require('./chunk-ZYFTT6DB.cjs');
|
|
5
|
+
var chunkDKLLPKZN_cjs = require('./chunk-DKLLPKZN.cjs');
|
|
6
|
+
var React2 = require('react');
|
|
4
7
|
|
|
5
8
|
function _interopNamespace(e) {
|
|
6
9
|
if (e && e.__esModule) return e;
|
|
@@ -20,7 +23,7 @@ function _interopNamespace(e) {
|
|
|
20
23
|
return Object.freeze(n);
|
|
21
24
|
}
|
|
22
25
|
|
|
23
|
-
var
|
|
26
|
+
var React2__namespace = /*#__PURE__*/_interopNamespace(React2);
|
|
24
27
|
|
|
25
28
|
// src/integration/ContactFormSerializer.ts
|
|
26
29
|
var STANDARD_FIELDS = [
|
|
@@ -161,7 +164,7 @@ function deserializeErrors(railsErrors) {
|
|
|
161
164
|
}
|
|
162
165
|
return formErrors;
|
|
163
166
|
}
|
|
164
|
-
var BlockErrorBoundary = class extends
|
|
167
|
+
var BlockErrorBoundary = class extends React2__namespace.Component {
|
|
165
168
|
constructor(props) {
|
|
166
169
|
super(props);
|
|
167
170
|
this.state = { error: null };
|
|
@@ -181,16 +184,16 @@ var BlockErrorBoundary = class extends React__namespace.Component {
|
|
|
181
184
|
if (this.props.fallback) {
|
|
182
185
|
return this.props.fallback(this.state.error, this.props.block);
|
|
183
186
|
}
|
|
184
|
-
return /* @__PURE__ */
|
|
187
|
+
return /* @__PURE__ */ React2__namespace.createElement(
|
|
185
188
|
"div",
|
|
186
189
|
{
|
|
187
190
|
className: "block-error border border-destructive bg-destructive p-4 rounded text-destructive-foreground",
|
|
188
191
|
"data-block-id": this.props.block._id,
|
|
189
192
|
"data-block-type": this.props.block._type
|
|
190
193
|
},
|
|
191
|
-
/* @__PURE__ */
|
|
192
|
-
/* @__PURE__ */
|
|
193
|
-
/* @__PURE__ */
|
|
194
|
+
/* @__PURE__ */ React2__namespace.createElement("p", { className: "font-semibold" }, "Block Render Error"),
|
|
195
|
+
/* @__PURE__ */ React2__namespace.createElement("p", { className: "text-sm" }, "Block: ", this.props.block._name || this.props.block._id, " (", this.props.block._type, ")"),
|
|
196
|
+
/* @__PURE__ */ React2__namespace.createElement("p", { className: "text-sm mt-1" }, this.state.error.message)
|
|
194
197
|
);
|
|
195
198
|
}
|
|
196
199
|
return this.props.children;
|
|
@@ -221,9 +224,9 @@ function createBlockAdapter(Component2, options = {}) {
|
|
|
221
224
|
...dataAttrs
|
|
222
225
|
};
|
|
223
226
|
const renderedChildren = renderChildren ? renderChildren(block._id) : children;
|
|
224
|
-
const element = /* @__PURE__ */
|
|
227
|
+
const element = /* @__PURE__ */ React2__namespace.createElement(Component2, { ...componentProps }, renderedChildren);
|
|
225
228
|
if (withErrorBoundary) {
|
|
226
|
-
return /* @__PURE__ */
|
|
229
|
+
return /* @__PURE__ */ React2__namespace.createElement(BlockErrorBoundary, { block, fallback: errorFallback }, element);
|
|
227
230
|
}
|
|
228
231
|
return element;
|
|
229
232
|
};
|
|
@@ -248,10 +251,572 @@ function createBlockAdapters(components, options = {}) {
|
|
|
248
251
|
return adapted;
|
|
249
252
|
}
|
|
250
253
|
|
|
254
|
+
// src/integration/form-submit.ts
|
|
255
|
+
var PageSpeedFormSubmissionError = class extends Error {
|
|
256
|
+
constructor(message, options = {}) {
|
|
257
|
+
super(message);
|
|
258
|
+
this.name = "PageSpeedFormSubmissionError";
|
|
259
|
+
this.formErrors = options.formErrors;
|
|
260
|
+
this.status = options.status;
|
|
261
|
+
}
|
|
262
|
+
};
|
|
263
|
+
var EMAIL_REGEX = /^[^\s@]+@[^\s@]+\.[^\s@]+$/;
|
|
264
|
+
function isValidEmail(value) {
|
|
265
|
+
return EMAIL_REGEX.test(value);
|
|
266
|
+
}
|
|
267
|
+
function buildUrlWithParams(endpoint, values) {
|
|
268
|
+
const base = typeof window === "undefined" ? "http://localhost" : window.location.origin;
|
|
269
|
+
const url = new URL(endpoint, base);
|
|
270
|
+
Object.entries(values).forEach(([key, value]) => {
|
|
271
|
+
if (value === void 0 || value === null) return;
|
|
272
|
+
if (Array.isArray(value)) {
|
|
273
|
+
value.forEach((item) => {
|
|
274
|
+
if (item !== void 0 && item !== null) {
|
|
275
|
+
url.searchParams.append(key, String(item));
|
|
276
|
+
}
|
|
277
|
+
});
|
|
278
|
+
return;
|
|
279
|
+
}
|
|
280
|
+
url.searchParams.set(key, String(value));
|
|
281
|
+
});
|
|
282
|
+
return url.toString();
|
|
283
|
+
}
|
|
284
|
+
function normalizeMethod(method) {
|
|
285
|
+
return (method || "post").toUpperCase();
|
|
286
|
+
}
|
|
287
|
+
function resolveFormat(config) {
|
|
288
|
+
if (config?.format) return config.format;
|
|
289
|
+
return config?.apiKey ? "rails" : "json";
|
|
290
|
+
}
|
|
291
|
+
function mergeValues(values, config) {
|
|
292
|
+
return {
|
|
293
|
+
...config?.values ?? {},
|
|
294
|
+
...values
|
|
295
|
+
};
|
|
296
|
+
}
|
|
297
|
+
async function submitPageSpeedForm(values, config) {
|
|
298
|
+
if (!config?.endpoint) {
|
|
299
|
+
return null;
|
|
300
|
+
}
|
|
301
|
+
const payload = mergeValues(values, config);
|
|
302
|
+
const method = normalizeMethod(config.method);
|
|
303
|
+
const format = resolveFormat(config);
|
|
304
|
+
const headers = { ...config.headers ?? {} };
|
|
305
|
+
if (format === "rails") {
|
|
306
|
+
if (!config.apiKey) {
|
|
307
|
+
throw new PageSpeedFormSubmissionError(
|
|
308
|
+
"Missing apiKey for Rails form submission."
|
|
309
|
+
);
|
|
310
|
+
}
|
|
311
|
+
const railsConfig = {
|
|
312
|
+
apiKey: config.apiKey,
|
|
313
|
+
contactCategoryToken: config.contactCategoryToken,
|
|
314
|
+
locationId: config.locationId,
|
|
315
|
+
websiteId: config.websiteId,
|
|
316
|
+
websiteFormAssignmentId: config.websiteFormAssignmentId,
|
|
317
|
+
visitorIpAddress: config.visitorIpAddress
|
|
318
|
+
};
|
|
319
|
+
const serialized = serializeForRails(payload, railsConfig);
|
|
320
|
+
if (serialized.contact.contact_form_upload_tokens) {
|
|
321
|
+
serialized.contact.contact_form_upload_tokens = serialized.contact.contact_form_upload_tokens.map((token) => token.replace(/^upload_/, ""));
|
|
322
|
+
}
|
|
323
|
+
headers["Content-Type"] ?? (headers["Content-Type"] = "application/json");
|
|
324
|
+
const response2 = await fetch(config.endpoint, {
|
|
325
|
+
method,
|
|
326
|
+
headers,
|
|
327
|
+
body: JSON.stringify(serialized)
|
|
328
|
+
});
|
|
329
|
+
const data2 = await response2.json().catch(() => null);
|
|
330
|
+
if (!response2.ok || data2 && data2.errors) {
|
|
331
|
+
const errorResponse = {
|
|
332
|
+
errors: data2?.errors ?? { base: ["Form submission failed"] },
|
|
333
|
+
status: data2?.status ?? response2.status
|
|
334
|
+
};
|
|
335
|
+
const formErrors = deserializeErrors(errorResponse);
|
|
336
|
+
throw new PageSpeedFormSubmissionError("Form submission failed.", {
|
|
337
|
+
formErrors,
|
|
338
|
+
status: errorResponse.status
|
|
339
|
+
});
|
|
340
|
+
}
|
|
341
|
+
return data2;
|
|
342
|
+
}
|
|
343
|
+
if (method === "GET") {
|
|
344
|
+
const url = buildUrlWithParams(config.endpoint, payload);
|
|
345
|
+
const response2 = await fetch(url, { method, headers });
|
|
346
|
+
const data2 = await response2.json().catch(() => null);
|
|
347
|
+
if (!response2.ok) {
|
|
348
|
+
throw new PageSpeedFormSubmissionError(
|
|
349
|
+
data2?.message || "Form submission failed.",
|
|
350
|
+
{ status: response2.status }
|
|
351
|
+
);
|
|
352
|
+
}
|
|
353
|
+
return data2;
|
|
354
|
+
}
|
|
355
|
+
headers["Content-Type"] ?? (headers["Content-Type"] = "application/json");
|
|
356
|
+
const response = await fetch(config.endpoint, {
|
|
357
|
+
method,
|
|
358
|
+
headers,
|
|
359
|
+
body: JSON.stringify(payload)
|
|
360
|
+
});
|
|
361
|
+
const data = await response.json().catch(() => null);
|
|
362
|
+
if (!response.ok) {
|
|
363
|
+
throw new PageSpeedFormSubmissionError(
|
|
364
|
+
data?.message || "Form submission failed.",
|
|
365
|
+
{ status: response.status }
|
|
366
|
+
);
|
|
367
|
+
}
|
|
368
|
+
return data;
|
|
369
|
+
}
|
|
370
|
+
|
|
371
|
+
// src/integration/form-field-types.ts
|
|
372
|
+
function generateInitialValues(fields) {
|
|
373
|
+
return fields.reduce(
|
|
374
|
+
(acc, field) => {
|
|
375
|
+
if (field.type === "checkbox") {
|
|
376
|
+
acc[field.name] = false;
|
|
377
|
+
} else if (field.type === "checkbox-group" || field.type === "multi-select") {
|
|
378
|
+
acc[field.name] = [];
|
|
379
|
+
} else if (field.type === "file") {
|
|
380
|
+
acc[field.name] = [];
|
|
381
|
+
} else if (field.type === "date-range") {
|
|
382
|
+
acc[field.name] = { start: null, end: null };
|
|
383
|
+
} else {
|
|
384
|
+
acc[field.name] = "";
|
|
385
|
+
}
|
|
386
|
+
return acc;
|
|
387
|
+
},
|
|
388
|
+
{}
|
|
389
|
+
);
|
|
390
|
+
}
|
|
391
|
+
function generateValidationSchema(fields) {
|
|
392
|
+
return fields.reduce(
|
|
393
|
+
(acc, field) => {
|
|
394
|
+
acc[field.name] = (value, allValues) => {
|
|
395
|
+
if (field.required) {
|
|
396
|
+
if (!value || typeof value === "string" && !value.trim()) {
|
|
397
|
+
return `${field.label} is required`;
|
|
398
|
+
}
|
|
399
|
+
}
|
|
400
|
+
if (field.type === "email" && value) {
|
|
401
|
+
if (!/^[^\s@]+@[^\s@]+\.[^\s@]+$/.test(value)) {
|
|
402
|
+
return "Please enter a valid email address";
|
|
403
|
+
}
|
|
404
|
+
}
|
|
405
|
+
if (field.type === "url" && value) {
|
|
406
|
+
try {
|
|
407
|
+
new URL(value);
|
|
408
|
+
} catch {
|
|
409
|
+
return "Please enter a valid URL";
|
|
410
|
+
}
|
|
411
|
+
}
|
|
412
|
+
if (field.validator) {
|
|
413
|
+
return field.validator(value, allValues);
|
|
414
|
+
}
|
|
415
|
+
return void 0;
|
|
416
|
+
};
|
|
417
|
+
return acc;
|
|
418
|
+
},
|
|
419
|
+
{}
|
|
420
|
+
);
|
|
421
|
+
}
|
|
422
|
+
var columnSpanClasses = {
|
|
423
|
+
1: "col-span-12 md:col-span-1",
|
|
424
|
+
2: "col-span-12 md:col-span-2",
|
|
425
|
+
3: "col-span-12 md:col-span-3",
|
|
426
|
+
4: "col-span-12 md:col-span-4",
|
|
427
|
+
5: "col-span-12 md:col-span-5",
|
|
428
|
+
6: "col-span-12 md:col-span-6",
|
|
429
|
+
7: "col-span-12 md:col-span-7",
|
|
430
|
+
8: "col-span-12 md:col-span-8",
|
|
431
|
+
9: "col-span-12 md:col-span-9",
|
|
432
|
+
10: "col-span-12 md:col-span-10",
|
|
433
|
+
11: "col-span-12 md:col-span-11",
|
|
434
|
+
12: "col-span-12"
|
|
435
|
+
};
|
|
436
|
+
function getColumnSpanClass(span) {
|
|
437
|
+
if (!span || span === 12) return "col-span-12";
|
|
438
|
+
const clamped = Math.max(1, Math.min(span, 12));
|
|
439
|
+
return columnSpanClasses[clamped] || "col-span-12";
|
|
440
|
+
}
|
|
441
|
+
var DEFAULT_UPLOAD_ENDPOINT = "https://api.dashtrack.com/contacts/_/contact_form_uploads";
|
|
442
|
+
function useFileUpload(options) {
|
|
443
|
+
const [uploadTokens, setUploadTokens] = React2.useState([]);
|
|
444
|
+
const [uploadProgress, setUploadProgress] = React2.useState({});
|
|
445
|
+
const [isUploading, setIsUploading] = React2.useState(false);
|
|
446
|
+
const endpoint = options?.endpoint || DEFAULT_UPLOAD_ENDPOINT;
|
|
447
|
+
const uploadFiles = React2.useCallback(
|
|
448
|
+
async (files) => {
|
|
449
|
+
if (files.length === 0) return;
|
|
450
|
+
setIsUploading(true);
|
|
451
|
+
try {
|
|
452
|
+
const tokens = [];
|
|
453
|
+
for (const file of files) {
|
|
454
|
+
const formData = new FormData();
|
|
455
|
+
formData.append("contact_form_upload[file_upload]", file);
|
|
456
|
+
formData.append("contact_form_upload[title]", file.name);
|
|
457
|
+
formData.append("contact_form_upload[file_name]", file.name);
|
|
458
|
+
formData.append("contact_form_upload[file_size]", String(file.size));
|
|
459
|
+
const response = await fetch(endpoint, {
|
|
460
|
+
method: "POST",
|
|
461
|
+
body: formData
|
|
462
|
+
});
|
|
463
|
+
if (!response.ok) {
|
|
464
|
+
throw new Error(`Upload failed: ${response.statusText}`);
|
|
465
|
+
}
|
|
466
|
+
const data = await response.json();
|
|
467
|
+
if (data.contact_form_upload?.token) {
|
|
468
|
+
tokens.push(`upload_${data.contact_form_upload.token}`);
|
|
469
|
+
}
|
|
470
|
+
setUploadProgress((prev) => ({
|
|
471
|
+
...prev,
|
|
472
|
+
[file.name]: 100
|
|
473
|
+
}));
|
|
474
|
+
}
|
|
475
|
+
setUploadTokens(tokens);
|
|
476
|
+
} catch (error) {
|
|
477
|
+
options?.onError?.(error);
|
|
478
|
+
} finally {
|
|
479
|
+
setIsUploading(false);
|
|
480
|
+
}
|
|
481
|
+
},
|
|
482
|
+
[endpoint, options]
|
|
483
|
+
);
|
|
484
|
+
const removeFile = React2.useCallback((file, index) => {
|
|
485
|
+
setUploadTokens((prev) => prev.filter((_, i) => i !== index));
|
|
486
|
+
setUploadProgress((prev) => {
|
|
487
|
+
const next = { ...prev };
|
|
488
|
+
delete next[file.name];
|
|
489
|
+
return next;
|
|
490
|
+
});
|
|
491
|
+
}, []);
|
|
492
|
+
const resetUpload = React2.useCallback(() => {
|
|
493
|
+
setUploadTokens([]);
|
|
494
|
+
setUploadProgress({});
|
|
495
|
+
}, []);
|
|
496
|
+
return {
|
|
497
|
+
uploadTokens,
|
|
498
|
+
uploadProgress,
|
|
499
|
+
isUploading,
|
|
500
|
+
uploadFiles,
|
|
501
|
+
removeFile,
|
|
502
|
+
resetUpload
|
|
503
|
+
};
|
|
504
|
+
}
|
|
505
|
+
function resolveRedirect(redirectUrl) {
|
|
506
|
+
const trimmed = redirectUrl.trim();
|
|
507
|
+
if (trimmed.startsWith("/") && !trimmed.startsWith("//")) {
|
|
508
|
+
return { destination: trimmed, internalHref: trimmed };
|
|
509
|
+
}
|
|
510
|
+
if (typeof window === "undefined") {
|
|
511
|
+
return { destination: trimmed };
|
|
512
|
+
}
|
|
513
|
+
try {
|
|
514
|
+
const url = new URL(trimmed, window.location.href);
|
|
515
|
+
if (url.origin === window.location.origin) {
|
|
516
|
+
return {
|
|
517
|
+
destination: url.toString(),
|
|
518
|
+
internalHref: `${url.pathname}${url.search}${url.hash}`
|
|
519
|
+
};
|
|
520
|
+
}
|
|
521
|
+
return { destination: url.toString() };
|
|
522
|
+
} catch {
|
|
523
|
+
return { destination: trimmed };
|
|
524
|
+
}
|
|
525
|
+
}
|
|
526
|
+
function useContactForm(options) {
|
|
527
|
+
const {
|
|
528
|
+
formFields,
|
|
529
|
+
formConfig,
|
|
530
|
+
onSubmit,
|
|
531
|
+
onSuccess,
|
|
532
|
+
onError,
|
|
533
|
+
resetOnSuccess = true,
|
|
534
|
+
uploadTokens = [],
|
|
535
|
+
navigate
|
|
536
|
+
} = options;
|
|
537
|
+
const [submissionError, setSubmissionError] = React2.useState(null);
|
|
538
|
+
const submissionConfig = formConfig?.submissionConfig;
|
|
539
|
+
const redirectUrl = submissionConfig?.redirectUrl;
|
|
540
|
+
const resetSubmissionState = React2.useCallback(() => {
|
|
541
|
+
setSubmissionError(null);
|
|
542
|
+
}, []);
|
|
543
|
+
const performRedirect = React2.useCallback(() => {
|
|
544
|
+
if (!redirectUrl || typeof window === "undefined") {
|
|
545
|
+
return;
|
|
546
|
+
}
|
|
547
|
+
const { destination, internalHref } = resolveRedirect(redirectUrl);
|
|
548
|
+
const attemptInternalNavigation = () => {
|
|
549
|
+
if (!internalHref) return false;
|
|
550
|
+
if (navigate) {
|
|
551
|
+
return navigate(internalHref) !== false;
|
|
552
|
+
}
|
|
553
|
+
const handler = window.__opensiteNavigationHandler;
|
|
554
|
+
if (typeof handler === "function") {
|
|
555
|
+
try {
|
|
556
|
+
return handler(internalHref, void 0) !== false;
|
|
557
|
+
} catch {
|
|
558
|
+
return false;
|
|
559
|
+
}
|
|
560
|
+
}
|
|
561
|
+
return false;
|
|
562
|
+
};
|
|
563
|
+
window.setTimeout(() => {
|
|
564
|
+
if (attemptInternalNavigation()) return;
|
|
565
|
+
window.location.assign(destination);
|
|
566
|
+
}, 150);
|
|
567
|
+
}, [navigate, redirectUrl]);
|
|
568
|
+
const form = chunkEX6CRLKG_cjs.useForm({
|
|
569
|
+
initialValues: React2.useMemo(
|
|
570
|
+
() => generateInitialValues(formFields),
|
|
571
|
+
[formFields]
|
|
572
|
+
),
|
|
573
|
+
validationSchema: React2.useMemo(
|
|
574
|
+
() => generateValidationSchema(formFields),
|
|
575
|
+
[formFields]
|
|
576
|
+
),
|
|
577
|
+
onSubmit: async (values, helpers) => {
|
|
578
|
+
resetSubmissionState();
|
|
579
|
+
const shouldAutoSubmit = Boolean(formConfig?.endpoint);
|
|
580
|
+
if (!shouldAutoSubmit && !onSubmit) {
|
|
581
|
+
return;
|
|
582
|
+
}
|
|
583
|
+
try {
|
|
584
|
+
let result;
|
|
585
|
+
const submissionValues = {
|
|
586
|
+
...values,
|
|
587
|
+
...uploadTokens.length > 0 && {
|
|
588
|
+
contact_form_upload_tokens: uploadTokens
|
|
589
|
+
}
|
|
590
|
+
};
|
|
591
|
+
if (shouldAutoSubmit) {
|
|
592
|
+
result = await submitPageSpeedForm(submissionValues, formConfig);
|
|
593
|
+
}
|
|
594
|
+
if (onSubmit) {
|
|
595
|
+
await onSubmit(submissionValues);
|
|
596
|
+
}
|
|
597
|
+
if (shouldAutoSubmit || onSubmit) {
|
|
598
|
+
try {
|
|
599
|
+
await submissionConfig?.handleFormSubmission?.({
|
|
600
|
+
formData: submissionValues,
|
|
601
|
+
responseData: result
|
|
602
|
+
});
|
|
603
|
+
} catch {
|
|
604
|
+
}
|
|
605
|
+
if (resetOnSuccess) {
|
|
606
|
+
helpers.resetForm();
|
|
607
|
+
}
|
|
608
|
+
onSuccess?.(result);
|
|
609
|
+
if (submissionConfig?.behavior === "redirect" && submissionConfig.redirectUrl) {
|
|
610
|
+
performRedirect();
|
|
611
|
+
}
|
|
612
|
+
}
|
|
613
|
+
} catch (error) {
|
|
614
|
+
if (error instanceof PageSpeedFormSubmissionError && error.formErrors) {
|
|
615
|
+
helpers.setErrors(error.formErrors);
|
|
616
|
+
}
|
|
617
|
+
const errorMessage = error instanceof Error ? error.message : "Form submission failed";
|
|
618
|
+
setSubmissionError(errorMessage);
|
|
619
|
+
onError?.(error);
|
|
620
|
+
}
|
|
621
|
+
}
|
|
622
|
+
});
|
|
623
|
+
const formMethod = formConfig?.method?.toLowerCase() === "get" ? "get" : "post";
|
|
624
|
+
return {
|
|
625
|
+
form,
|
|
626
|
+
isSubmitted: form.status === "success",
|
|
627
|
+
submissionError,
|
|
628
|
+
formMethod,
|
|
629
|
+
resetSubmissionState
|
|
630
|
+
};
|
|
631
|
+
}
|
|
632
|
+
function DynamicFormField({
|
|
633
|
+
field,
|
|
634
|
+
className,
|
|
635
|
+
uploadProgress = {},
|
|
636
|
+
onFileUpload,
|
|
637
|
+
onFileRemove,
|
|
638
|
+
isUploading = false
|
|
639
|
+
}) {
|
|
640
|
+
const fieldId = field.name;
|
|
641
|
+
const usesGroupLegend = field.type === "radio" || field.type === "checkbox-group";
|
|
642
|
+
const usesInlineCheckboxLabel = field.type === "checkbox";
|
|
643
|
+
const shouldRenderFieldLabel = !usesGroupLegend && !usesInlineCheckboxLabel;
|
|
644
|
+
return /* @__PURE__ */ React2__namespace.createElement(
|
|
645
|
+
chunkEX6CRLKG_cjs.Field,
|
|
646
|
+
{
|
|
647
|
+
name: field.name,
|
|
648
|
+
label: shouldRenderFieldLabel ? field.label : void 0,
|
|
649
|
+
description: shouldRenderFieldLabel ? field.description : void 0,
|
|
650
|
+
required: field.required,
|
|
651
|
+
className: chunkDKLLPKZN_cjs.cn("space-y-2", className)
|
|
652
|
+
},
|
|
653
|
+
({ field: formField, meta }) => /* @__PURE__ */ React2__namespace.createElement("div", null, (field.type === "text" || field.type === "email" || field.type === "tel" || field.type === "search" || field.type === "password" || field.type === "url") && /* @__PURE__ */ React2__namespace.createElement(
|
|
654
|
+
chunkZYFTT6DB_cjs.TextInput,
|
|
655
|
+
{
|
|
656
|
+
...formField,
|
|
657
|
+
id: fieldId,
|
|
658
|
+
type: field.type,
|
|
659
|
+
placeholder: field.placeholder,
|
|
660
|
+
error: meta.touched && !!meta.error,
|
|
661
|
+
disabled: field.disabled,
|
|
662
|
+
"aria-label": field.label
|
|
663
|
+
}
|
|
664
|
+
), field.type === "number" && /* @__PURE__ */ React2__namespace.createElement(
|
|
665
|
+
chunkZYFTT6DB_cjs.TextInput,
|
|
666
|
+
{
|
|
667
|
+
...formField,
|
|
668
|
+
id: fieldId,
|
|
669
|
+
type: "text",
|
|
670
|
+
placeholder: field.placeholder,
|
|
671
|
+
error: meta.touched && !!meta.error,
|
|
672
|
+
disabled: field.disabled,
|
|
673
|
+
"aria-label": field.label
|
|
674
|
+
}
|
|
675
|
+
), field.type === "textarea" && /* @__PURE__ */ React2__namespace.createElement(
|
|
676
|
+
chunkZYFTT6DB_cjs.TextArea,
|
|
677
|
+
{
|
|
678
|
+
...formField,
|
|
679
|
+
id: fieldId,
|
|
680
|
+
placeholder: field.placeholder,
|
|
681
|
+
rows: field.rows || 4,
|
|
682
|
+
error: meta.touched && !!meta.error,
|
|
683
|
+
disabled: field.disabled,
|
|
684
|
+
"aria-label": field.label
|
|
685
|
+
}
|
|
686
|
+
), field.type === "select" && field.options && /* @__PURE__ */ React2__namespace.createElement(
|
|
687
|
+
chunkZYFTT6DB_cjs.Select,
|
|
688
|
+
{
|
|
689
|
+
...formField,
|
|
690
|
+
id: fieldId,
|
|
691
|
+
options: field.options,
|
|
692
|
+
placeholder: field.placeholder || `Select ${field.label.toLowerCase()}`,
|
|
693
|
+
error: meta.touched && !!meta.error,
|
|
694
|
+
disabled: field.disabled,
|
|
695
|
+
"aria-label": field.label
|
|
696
|
+
}
|
|
697
|
+
), field.type === "multi-select" && field.options && /* @__PURE__ */ React2__namespace.createElement(
|
|
698
|
+
chunkZYFTT6DB_cjs.MultiSelect,
|
|
699
|
+
{
|
|
700
|
+
...formField,
|
|
701
|
+
id: fieldId,
|
|
702
|
+
options: field.options,
|
|
703
|
+
placeholder: field.placeholder || `Select ${field.label.toLowerCase()}`,
|
|
704
|
+
error: meta.touched && !!meta.error,
|
|
705
|
+
disabled: field.disabled,
|
|
706
|
+
"aria-label": field.label
|
|
707
|
+
}
|
|
708
|
+
), field.type === "radio" && field.options && /* @__PURE__ */ React2__namespace.createElement(
|
|
709
|
+
chunkZYFTT6DB_cjs.Radio,
|
|
710
|
+
{
|
|
711
|
+
...formField,
|
|
712
|
+
id: fieldId,
|
|
713
|
+
options: field.options,
|
|
714
|
+
label: field.label,
|
|
715
|
+
description: field.description,
|
|
716
|
+
required: field.required,
|
|
717
|
+
disabled: field.disabled,
|
|
718
|
+
layout: field.layout || "stacked",
|
|
719
|
+
error: meta.touched && !!meta.error,
|
|
720
|
+
"aria-label": field.label
|
|
721
|
+
}
|
|
722
|
+
), field.type === "checkbox" && /* @__PURE__ */ React2__namespace.createElement(
|
|
723
|
+
chunkZYFTT6DB_cjs.Checkbox,
|
|
724
|
+
{
|
|
725
|
+
...formField,
|
|
726
|
+
id: fieldId,
|
|
727
|
+
value: formField.value === true || formField.value === "true",
|
|
728
|
+
onChange: (checked) => formField.onChange(checked),
|
|
729
|
+
label: field.label,
|
|
730
|
+
description: field.description,
|
|
731
|
+
disabled: field.disabled,
|
|
732
|
+
required: field.required,
|
|
733
|
+
error: meta.touched && !!meta.error,
|
|
734
|
+
"aria-label": field.label
|
|
735
|
+
}
|
|
736
|
+
), field.type === "checkbox-group" && field.options && /* @__PURE__ */ React2__namespace.createElement(
|
|
737
|
+
chunkZYFTT6DB_cjs.CheckboxGroup,
|
|
738
|
+
{
|
|
739
|
+
...formField,
|
|
740
|
+
id: fieldId,
|
|
741
|
+
options: field.options,
|
|
742
|
+
label: field.label,
|
|
743
|
+
description: field.description,
|
|
744
|
+
required: field.required,
|
|
745
|
+
disabled: field.disabled,
|
|
746
|
+
layout: field.layout || "stacked",
|
|
747
|
+
error: meta.touched && !!meta.error,
|
|
748
|
+
"aria-label": field.label
|
|
749
|
+
}
|
|
750
|
+
), (field.type === "date-picker" || field.type === "date") && /* @__PURE__ */ React2__namespace.createElement(
|
|
751
|
+
chunkZYFTT6DB_cjs.DatePicker,
|
|
752
|
+
{
|
|
753
|
+
...formField,
|
|
754
|
+
id: fieldId,
|
|
755
|
+
placeholder: field.placeholder,
|
|
756
|
+
error: meta.touched && !!meta.error,
|
|
757
|
+
disabled: field.disabled,
|
|
758
|
+
"aria-label": field.label
|
|
759
|
+
}
|
|
760
|
+
), field.type === "date-range" && /* @__PURE__ */ React2__namespace.createElement(
|
|
761
|
+
chunkZYFTT6DB_cjs.DateRangePicker,
|
|
762
|
+
{
|
|
763
|
+
...formField,
|
|
764
|
+
id: fieldId,
|
|
765
|
+
placeholder: field.placeholder,
|
|
766
|
+
error: meta.touched && !!meta.error,
|
|
767
|
+
disabled: field.disabled,
|
|
768
|
+
"aria-label": field.label
|
|
769
|
+
}
|
|
770
|
+
), field.type === "time" && /* @__PURE__ */ React2__namespace.createElement(
|
|
771
|
+
chunkZYFTT6DB_cjs.TimePicker,
|
|
772
|
+
{
|
|
773
|
+
...formField,
|
|
774
|
+
id: fieldId,
|
|
775
|
+
placeholder: field.placeholder,
|
|
776
|
+
error: meta.touched && !!meta.error,
|
|
777
|
+
disabled: field.disabled,
|
|
778
|
+
"aria-label": field.label
|
|
779
|
+
}
|
|
780
|
+
), field.type === "file" && /* @__PURE__ */ React2__namespace.createElement(
|
|
781
|
+
chunkZYFTT6DB_cjs.FileInput,
|
|
782
|
+
{
|
|
783
|
+
...formField,
|
|
784
|
+
id: fieldId,
|
|
785
|
+
accept: field.accept,
|
|
786
|
+
maxSize: field.maxSize || 5 * 1024 * 1024,
|
|
787
|
+
maxFiles: field.maxFiles || 1,
|
|
788
|
+
multiple: field.multiple || false,
|
|
789
|
+
placeholder: field.placeholder || "Choose file(s)...",
|
|
790
|
+
error: meta.touched && !!meta.error,
|
|
791
|
+
disabled: field.disabled || isUploading,
|
|
792
|
+
showProgress: true,
|
|
793
|
+
uploadProgress,
|
|
794
|
+
onChange: (files) => {
|
|
795
|
+
formField.onChange(files);
|
|
796
|
+
if (files.length > 0 && onFileUpload) {
|
|
797
|
+
onFileUpload(files);
|
|
798
|
+
}
|
|
799
|
+
},
|
|
800
|
+
onFileRemove,
|
|
801
|
+
"aria-label": field.label
|
|
802
|
+
}
|
|
803
|
+
))
|
|
804
|
+
);
|
|
805
|
+
}
|
|
806
|
+
|
|
807
|
+
exports.DynamicFormField = DynamicFormField;
|
|
808
|
+
exports.PageSpeedFormSubmissionError = PageSpeedFormSubmissionError;
|
|
251
809
|
exports.createBlockAdapter = createBlockAdapter;
|
|
252
810
|
exports.createBlockAdapters = createBlockAdapters;
|
|
253
811
|
exports.deserializeErrors = deserializeErrors;
|
|
812
|
+
exports.generateInitialValues = generateInitialValues;
|
|
813
|
+
exports.generateValidationSchema = generateValidationSchema;
|
|
814
|
+
exports.getColumnSpanClass = getColumnSpanClass;
|
|
815
|
+
exports.isValidEmail = isValidEmail;
|
|
254
816
|
exports.serializeForRails = serializeForRails;
|
|
255
817
|
exports.standardInputTransformer = standardInputTransformer;
|
|
818
|
+
exports.submitPageSpeedForm = submitPageSpeedForm;
|
|
819
|
+
exports.useContactForm = useContactForm;
|
|
820
|
+
exports.useFileUpload = useFileUpload;
|
|
256
821
|
//# sourceMappingURL=integration.cjs.map
|
|
257
822
|
//# sourceMappingURL=integration.cjs.map
|