@opensite/ui 1.7.5 → 1.7.6
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/contact-dark.cjs +503 -124
- package/dist/contact-dark.d.cts +28 -9
- package/dist/contact-dark.d.ts +28 -9
- package/dist/contact-dark.js +505 -126
- package/dist/contact-faq.cjs +482 -104
- package/dist/contact-faq.d.cts +27 -17
- package/dist/contact-faq.d.ts +27 -17
- package/dist/contact-faq.js +484 -106
- package/dist/contact-photography.cjs +485 -116
- package/dist/contact-photography.d.cts +22 -9
- package/dist/contact-photography.d.ts +22 -9
- package/dist/contact-photography.js +487 -118
- package/dist/form-field-types-BYdJNOsW.d.cts +92 -0
- package/dist/form-field-types-BYdJNOsW.d.ts +92 -0
- package/dist/registry.cjs +2397 -2037
- package/dist/registry.js +1141 -781
- package/package.json +6 -1
package/dist/contact-faq.cjs
CHANGED
|
@@ -3,11 +3,11 @@
|
|
|
3
3
|
|
|
4
4
|
var React = require('react');
|
|
5
5
|
var forms = require('@page-speed/forms');
|
|
6
|
-
var inputs = require('@page-speed/forms/inputs');
|
|
7
6
|
var clsx = require('clsx');
|
|
8
7
|
var tailwindMerge = require('tailwind-merge');
|
|
9
8
|
var classVarianceAuthority = require('class-variance-authority');
|
|
10
9
|
var jsxRuntime = require('react/jsx-runtime');
|
|
10
|
+
var inputs = require('@page-speed/forms/inputs');
|
|
11
11
|
var LabelPrimitive = require('@radix-ui/react-label');
|
|
12
12
|
var AccordionPrimitive = require('@radix-ui/react-accordion');
|
|
13
13
|
var integration = require('@page-speed/forms/integration');
|
|
@@ -495,6 +495,200 @@ function Label({
|
|
|
495
495
|
}
|
|
496
496
|
);
|
|
497
497
|
}
|
|
498
|
+
function DynamicFormField({
|
|
499
|
+
field,
|
|
500
|
+
className,
|
|
501
|
+
uploadProgress = {},
|
|
502
|
+
onFileUpload,
|
|
503
|
+
onFileRemove,
|
|
504
|
+
isUploading = false
|
|
505
|
+
}) {
|
|
506
|
+
const fieldId = `field-${field.name}`;
|
|
507
|
+
return /* @__PURE__ */ jsxRuntime.jsx(forms.Field, { name: field.name, children: ({ field: formField, meta }) => /* @__PURE__ */ jsxRuntime.jsxs("div", { className: cn("space-y-2", className), children: [
|
|
508
|
+
field.type !== "checkbox" && /* @__PURE__ */ jsxRuntime.jsxs(Label, { htmlFor: fieldId, children: [
|
|
509
|
+
field.label,
|
|
510
|
+
field.required && /* @__PURE__ */ jsxRuntime.jsx("span", { className: "text-destructive ml-1", children: "*" })
|
|
511
|
+
] }),
|
|
512
|
+
(field.type === "text" || field.type === "email" || field.type === "tel" || field.type === "search" || field.type === "password" || field.type === "url") && /* @__PURE__ */ jsxRuntime.jsx(
|
|
513
|
+
inputs.TextInput,
|
|
514
|
+
{
|
|
515
|
+
...formField,
|
|
516
|
+
id: fieldId,
|
|
517
|
+
type: field.type,
|
|
518
|
+
placeholder: field.placeholder,
|
|
519
|
+
error: meta.touched && !!meta.error,
|
|
520
|
+
disabled: field.disabled,
|
|
521
|
+
"aria-label": field.label
|
|
522
|
+
}
|
|
523
|
+
),
|
|
524
|
+
field.type === "number" && /* @__PURE__ */ jsxRuntime.jsx(
|
|
525
|
+
inputs.TextInput,
|
|
526
|
+
{
|
|
527
|
+
...formField,
|
|
528
|
+
id: fieldId,
|
|
529
|
+
type: "text",
|
|
530
|
+
placeholder: field.placeholder,
|
|
531
|
+
error: meta.touched && !!meta.error,
|
|
532
|
+
disabled: field.disabled,
|
|
533
|
+
"aria-label": field.label
|
|
534
|
+
}
|
|
535
|
+
),
|
|
536
|
+
field.type === "textarea" && /* @__PURE__ */ jsxRuntime.jsx(
|
|
537
|
+
inputs.TextArea,
|
|
538
|
+
{
|
|
539
|
+
...formField,
|
|
540
|
+
id: fieldId,
|
|
541
|
+
placeholder: field.placeholder,
|
|
542
|
+
rows: field.rows || 4,
|
|
543
|
+
error: meta.touched && !!meta.error,
|
|
544
|
+
disabled: field.disabled,
|
|
545
|
+
"aria-label": field.label
|
|
546
|
+
}
|
|
547
|
+
),
|
|
548
|
+
field.type === "select" && field.options && /* @__PURE__ */ jsxRuntime.jsx(
|
|
549
|
+
inputs.Select,
|
|
550
|
+
{
|
|
551
|
+
...formField,
|
|
552
|
+
id: fieldId,
|
|
553
|
+
options: field.options,
|
|
554
|
+
placeholder: field.placeholder || `Select ${field.label.toLowerCase()}`,
|
|
555
|
+
error: meta.touched && !!meta.error,
|
|
556
|
+
disabled: field.disabled,
|
|
557
|
+
"aria-label": field.label
|
|
558
|
+
}
|
|
559
|
+
),
|
|
560
|
+
field.type === "multi-select" && field.options && /* @__PURE__ */ jsxRuntime.jsx(
|
|
561
|
+
inputs.Select,
|
|
562
|
+
{
|
|
563
|
+
...formField,
|
|
564
|
+
id: fieldId,
|
|
565
|
+
options: field.options,
|
|
566
|
+
placeholder: field.placeholder || `Select ${field.label.toLowerCase()}`,
|
|
567
|
+
error: meta.touched && !!meta.error,
|
|
568
|
+
disabled: field.disabled,
|
|
569
|
+
"aria-label": field.label,
|
|
570
|
+
multiple: true
|
|
571
|
+
}
|
|
572
|
+
),
|
|
573
|
+
field.type === "radio" && field.options && /* @__PURE__ */ jsxRuntime.jsx(
|
|
574
|
+
inputs.Radio,
|
|
575
|
+
{
|
|
576
|
+
...formField,
|
|
577
|
+
id: fieldId,
|
|
578
|
+
options: field.options,
|
|
579
|
+
disabled: field.disabled,
|
|
580
|
+
layout: field.layout || "stacked",
|
|
581
|
+
error: meta.touched && !!meta.error,
|
|
582
|
+
"aria-label": field.label
|
|
583
|
+
}
|
|
584
|
+
),
|
|
585
|
+
field.type === "checkbox" && /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "flex items-start space-x-2", children: [
|
|
586
|
+
/* @__PURE__ */ jsxRuntime.jsx(
|
|
587
|
+
inputs.Checkbox,
|
|
588
|
+
{
|
|
589
|
+
...formField,
|
|
590
|
+
id: fieldId,
|
|
591
|
+
value: formField.value === true || formField.value === "true",
|
|
592
|
+
onChange: (checked) => formField.onChange(checked),
|
|
593
|
+
disabled: field.disabled,
|
|
594
|
+
error: meta.touched && !!meta.error,
|
|
595
|
+
"aria-label": field.label
|
|
596
|
+
}
|
|
597
|
+
),
|
|
598
|
+
/* @__PURE__ */ jsxRuntime.jsxs(
|
|
599
|
+
Label,
|
|
600
|
+
{
|
|
601
|
+
htmlFor: fieldId,
|
|
602
|
+
className: "font-normal cursor-pointer leading-relaxed",
|
|
603
|
+
children: [
|
|
604
|
+
field.label,
|
|
605
|
+
field.required && /* @__PURE__ */ jsxRuntime.jsx("span", { className: "text-destructive ml-1", children: "*" })
|
|
606
|
+
]
|
|
607
|
+
}
|
|
608
|
+
)
|
|
609
|
+
] }),
|
|
610
|
+
field.type === "checkbox-group" && field.options && /* @__PURE__ */ jsxRuntime.jsx(
|
|
611
|
+
inputs.CheckboxGroup,
|
|
612
|
+
{
|
|
613
|
+
...formField,
|
|
614
|
+
id: fieldId,
|
|
615
|
+
options: field.options,
|
|
616
|
+
disabled: field.disabled,
|
|
617
|
+
layout: field.layout || "stacked",
|
|
618
|
+
error: meta.touched && !!meta.error,
|
|
619
|
+
"aria-label": field.label
|
|
620
|
+
}
|
|
621
|
+
),
|
|
622
|
+
(field.type === "date-picker" || field.type === "date") && /* @__PURE__ */ jsxRuntime.jsx(
|
|
623
|
+
inputs.DatePicker,
|
|
624
|
+
{
|
|
625
|
+
...formField,
|
|
626
|
+
id: fieldId,
|
|
627
|
+
placeholder: field.placeholder,
|
|
628
|
+
error: meta.touched && !!meta.error,
|
|
629
|
+
disabled: field.disabled,
|
|
630
|
+
"aria-label": field.label
|
|
631
|
+
}
|
|
632
|
+
),
|
|
633
|
+
field.type === "date-range" && /* @__PURE__ */ jsxRuntime.jsx(
|
|
634
|
+
inputs.DateRangePicker,
|
|
635
|
+
{
|
|
636
|
+
...formField,
|
|
637
|
+
id: fieldId,
|
|
638
|
+
error: meta.touched && !!meta.error,
|
|
639
|
+
disabled: field.disabled,
|
|
640
|
+
"aria-label": field.label
|
|
641
|
+
}
|
|
642
|
+
),
|
|
643
|
+
field.type === "time" && /* @__PURE__ */ jsxRuntime.jsx(
|
|
644
|
+
inputs.TimePicker,
|
|
645
|
+
{
|
|
646
|
+
...formField,
|
|
647
|
+
id: fieldId,
|
|
648
|
+
placeholder: field.placeholder,
|
|
649
|
+
error: meta.touched && !!meta.error,
|
|
650
|
+
disabled: field.disabled,
|
|
651
|
+
"aria-label": field.label
|
|
652
|
+
}
|
|
653
|
+
),
|
|
654
|
+
field.type === "file" && /* @__PURE__ */ jsxRuntime.jsx(
|
|
655
|
+
inputs.FileInput,
|
|
656
|
+
{
|
|
657
|
+
...formField,
|
|
658
|
+
id: fieldId,
|
|
659
|
+
accept: field.accept,
|
|
660
|
+
maxSize: field.maxSize || 5 * 1024 * 1024,
|
|
661
|
+
maxFiles: field.maxFiles || 1,
|
|
662
|
+
multiple: field.multiple || false,
|
|
663
|
+
placeholder: field.placeholder || "Choose file(s)...",
|
|
664
|
+
error: meta.touched && !!meta.error,
|
|
665
|
+
disabled: field.disabled || isUploading,
|
|
666
|
+
showProgress: true,
|
|
667
|
+
uploadProgress,
|
|
668
|
+
onChange: (files) => {
|
|
669
|
+
formField.onChange(files);
|
|
670
|
+
if (files.length > 0 && onFileUpload) {
|
|
671
|
+
onFileUpload(files);
|
|
672
|
+
}
|
|
673
|
+
},
|
|
674
|
+
onFileRemove,
|
|
675
|
+
"aria-label": field.label
|
|
676
|
+
}
|
|
677
|
+
),
|
|
678
|
+
field.type === "rich-text" && /* @__PURE__ */ jsxRuntime.jsx(
|
|
679
|
+
inputs.RichTextEditor,
|
|
680
|
+
{
|
|
681
|
+
...formField,
|
|
682
|
+
id: fieldId,
|
|
683
|
+
placeholder: field.placeholder,
|
|
684
|
+
error: meta.touched && !!meta.error,
|
|
685
|
+
disabled: field.disabled,
|
|
686
|
+
"aria-label": field.label
|
|
687
|
+
}
|
|
688
|
+
),
|
|
689
|
+
meta.touched && meta.error && /* @__PURE__ */ jsxRuntime.jsx("p", { className: "text-sm text-destructive", children: meta.error })
|
|
690
|
+
] }) });
|
|
691
|
+
}
|
|
498
692
|
var svgCache = /* @__PURE__ */ new Map();
|
|
499
693
|
function DynamicIcon({
|
|
500
694
|
name,
|
|
@@ -660,6 +854,197 @@ function AccordionContent({
|
|
|
660
854
|
}
|
|
661
855
|
);
|
|
662
856
|
}
|
|
857
|
+
function useFileUpload(options) {
|
|
858
|
+
const [uploadTokens, setUploadTokens] = React.useState([]);
|
|
859
|
+
const [uploadProgress, setUploadProgress] = React.useState({});
|
|
860
|
+
const [isUploading, setIsUploading] = React.useState(false);
|
|
861
|
+
const endpoint = options?.endpoint || "https://api.dashtrack.com/contacts/_/contact_form_uploads";
|
|
862
|
+
const uploadFiles = React.useCallback(
|
|
863
|
+
async (files) => {
|
|
864
|
+
if (files.length === 0) return;
|
|
865
|
+
setIsUploading(true);
|
|
866
|
+
try {
|
|
867
|
+
const tokens = [];
|
|
868
|
+
for (const file of files) {
|
|
869
|
+
const formData = new FormData();
|
|
870
|
+
formData.append("contact_form_upload[file_upload]", file);
|
|
871
|
+
formData.append("contact_form_upload[title]", file.name);
|
|
872
|
+
formData.append("contact_form_upload[file_name]", file.name);
|
|
873
|
+
formData.append("contact_form_upload[file_size]", String(file.size));
|
|
874
|
+
const response = await fetch(endpoint, {
|
|
875
|
+
method: "POST",
|
|
876
|
+
body: formData
|
|
877
|
+
});
|
|
878
|
+
if (!response.ok) {
|
|
879
|
+
throw new Error(`Upload failed: ${response.statusText}`);
|
|
880
|
+
}
|
|
881
|
+
const data = await response.json();
|
|
882
|
+
if (data.contact_form_upload?.token) {
|
|
883
|
+
tokens.push(`upload_${data.contact_form_upload.token}`);
|
|
884
|
+
}
|
|
885
|
+
setUploadProgress((prev) => ({
|
|
886
|
+
...prev,
|
|
887
|
+
[file.name]: 100
|
|
888
|
+
}));
|
|
889
|
+
}
|
|
890
|
+
setUploadTokens(tokens);
|
|
891
|
+
} catch (error) {
|
|
892
|
+
console.error("File upload error:", error);
|
|
893
|
+
options?.onError?.(error);
|
|
894
|
+
} finally {
|
|
895
|
+
setIsUploading(false);
|
|
896
|
+
}
|
|
897
|
+
},
|
|
898
|
+
[endpoint, options]
|
|
899
|
+
);
|
|
900
|
+
const removeFile = React.useCallback((file, index) => {
|
|
901
|
+
setUploadTokens((prev) => prev.filter((_, i) => i !== index));
|
|
902
|
+
setUploadProgress((prev) => {
|
|
903
|
+
const newProgress = { ...prev };
|
|
904
|
+
delete newProgress[file.name];
|
|
905
|
+
return newProgress;
|
|
906
|
+
});
|
|
907
|
+
}, []);
|
|
908
|
+
const resetUpload = React.useCallback(() => {
|
|
909
|
+
setUploadTokens([]);
|
|
910
|
+
setUploadProgress({});
|
|
911
|
+
}, []);
|
|
912
|
+
return {
|
|
913
|
+
uploadTokens,
|
|
914
|
+
uploadProgress,
|
|
915
|
+
isUploading,
|
|
916
|
+
uploadFiles,
|
|
917
|
+
removeFile,
|
|
918
|
+
resetUpload
|
|
919
|
+
};
|
|
920
|
+
}
|
|
921
|
+
|
|
922
|
+
// lib/form-field-types.ts
|
|
923
|
+
function generateInitialValues(fields) {
|
|
924
|
+
return fields.reduce(
|
|
925
|
+
(acc, field) => {
|
|
926
|
+
if (field.type === "checkbox") {
|
|
927
|
+
acc[field.name] = false;
|
|
928
|
+
} else if (field.type === "checkbox-group" || field.type === "multi-select") {
|
|
929
|
+
acc[field.name] = [];
|
|
930
|
+
} else if (field.type === "file") {
|
|
931
|
+
acc[field.name] = [];
|
|
932
|
+
} else if (field.type === "date-range") {
|
|
933
|
+
acc[field.name] = { start: null, end: null };
|
|
934
|
+
} else {
|
|
935
|
+
acc[field.name] = "";
|
|
936
|
+
}
|
|
937
|
+
return acc;
|
|
938
|
+
},
|
|
939
|
+
{}
|
|
940
|
+
);
|
|
941
|
+
}
|
|
942
|
+
function generateValidationSchema(fields) {
|
|
943
|
+
return fields.reduce(
|
|
944
|
+
(acc, field) => {
|
|
945
|
+
acc[field.name] = (value, allValues) => {
|
|
946
|
+
if (field.required) {
|
|
947
|
+
if (!value || typeof value === "string" && !value.trim()) {
|
|
948
|
+
return `${field.label} is required`;
|
|
949
|
+
}
|
|
950
|
+
}
|
|
951
|
+
if (field.type === "email" && value) {
|
|
952
|
+
if (!/^[^\s@]+@[^\s@]+\.[^\s@]+$/.test(value)) {
|
|
953
|
+
return "Please enter a valid email address";
|
|
954
|
+
}
|
|
955
|
+
}
|
|
956
|
+
if (field.type === "url" && value) {
|
|
957
|
+
try {
|
|
958
|
+
new URL(value);
|
|
959
|
+
} catch {
|
|
960
|
+
return "Please enter a valid URL";
|
|
961
|
+
}
|
|
962
|
+
}
|
|
963
|
+
if (field.validator) {
|
|
964
|
+
return field.validator(value, allValues);
|
|
965
|
+
}
|
|
966
|
+
return void 0;
|
|
967
|
+
};
|
|
968
|
+
return acc;
|
|
969
|
+
},
|
|
970
|
+
{}
|
|
971
|
+
);
|
|
972
|
+
}
|
|
973
|
+
function getColumnSpanClass(span) {
|
|
974
|
+
if (!span || span === 12) return "col-span-12";
|
|
975
|
+
return `col-span-12 sm:col-span-${Math.min(span, 12)}`;
|
|
976
|
+
}
|
|
977
|
+
|
|
978
|
+
// lib/forms/use-contact-form.ts
|
|
979
|
+
function useContactForm(options) {
|
|
980
|
+
const {
|
|
981
|
+
formFields,
|
|
982
|
+
formConfig,
|
|
983
|
+
onSubmit,
|
|
984
|
+
onSuccess,
|
|
985
|
+
onError,
|
|
986
|
+
resetOnSuccess = true,
|
|
987
|
+
uploadTokens = []
|
|
988
|
+
} = options;
|
|
989
|
+
const [isSubmitted, setIsSubmitted] = React.useState(false);
|
|
990
|
+
const [submissionError, setSubmissionError] = React.useState(null);
|
|
991
|
+
const form = forms.useForm({
|
|
992
|
+
initialValues: React.useMemo(
|
|
993
|
+
() => generateInitialValues(formFields),
|
|
994
|
+
[formFields]
|
|
995
|
+
),
|
|
996
|
+
validationSchema: React.useMemo(
|
|
997
|
+
() => generateValidationSchema(formFields),
|
|
998
|
+
[formFields]
|
|
999
|
+
),
|
|
1000
|
+
onSubmit: async (values, helpers) => {
|
|
1001
|
+
setSubmissionError(null);
|
|
1002
|
+
const shouldAutoSubmit = Boolean(formConfig?.endpoint);
|
|
1003
|
+
if (!shouldAutoSubmit && !onSubmit) {
|
|
1004
|
+
return;
|
|
1005
|
+
}
|
|
1006
|
+
try {
|
|
1007
|
+
let result;
|
|
1008
|
+
const submissionValues = {
|
|
1009
|
+
...values,
|
|
1010
|
+
...uploadTokens.length > 0 && {
|
|
1011
|
+
contact_form_upload_tokens: uploadTokens
|
|
1012
|
+
}
|
|
1013
|
+
};
|
|
1014
|
+
if (shouldAutoSubmit) {
|
|
1015
|
+
result = await submitPageSpeedForm(submissionValues, formConfig);
|
|
1016
|
+
}
|
|
1017
|
+
if (onSubmit) {
|
|
1018
|
+
await onSubmit(submissionValues);
|
|
1019
|
+
}
|
|
1020
|
+
if (shouldAutoSubmit || onSubmit) {
|
|
1021
|
+
setIsSubmitted(true);
|
|
1022
|
+
if (resetOnSuccess) {
|
|
1023
|
+
helpers.resetForm();
|
|
1024
|
+
}
|
|
1025
|
+
onSuccess?.(result);
|
|
1026
|
+
setTimeout(() => setIsSubmitted(false), 5e3);
|
|
1027
|
+
}
|
|
1028
|
+
} catch (error) {
|
|
1029
|
+
if (error instanceof PageSpeedFormSubmissionError && error.formErrors) {
|
|
1030
|
+
helpers.setErrors(error.formErrors);
|
|
1031
|
+
}
|
|
1032
|
+
const errorMessage = error instanceof Error ? error.message : "Form submission failed";
|
|
1033
|
+
setSubmissionError(errorMessage);
|
|
1034
|
+
onError?.(error);
|
|
1035
|
+
}
|
|
1036
|
+
}
|
|
1037
|
+
});
|
|
1038
|
+
const formMethod = formConfig?.method?.toLowerCase() === "get" ? "get" : "post";
|
|
1039
|
+
return {
|
|
1040
|
+
form,
|
|
1041
|
+
isSubmitted,
|
|
1042
|
+
submissionError,
|
|
1043
|
+
formMethod
|
|
1044
|
+
};
|
|
1045
|
+
}
|
|
1046
|
+
|
|
1047
|
+
// lib/forms.ts
|
|
663
1048
|
var PageSpeedFormSubmissionError = class extends Error {
|
|
664
1049
|
constructor(message, options = {}) {
|
|
665
1050
|
super(message);
|
|
@@ -1150,18 +1535,57 @@ var Section = React__namespace.default.forwardRef(
|
|
|
1150
1535
|
}
|
|
1151
1536
|
);
|
|
1152
1537
|
Section.displayName = "Section";
|
|
1538
|
+
var DEFAULT_FORM_FIELDS = [
|
|
1539
|
+
{
|
|
1540
|
+
name: "name",
|
|
1541
|
+
type: "text",
|
|
1542
|
+
label: "Name",
|
|
1543
|
+
placeholder: "Full Name",
|
|
1544
|
+
required: true,
|
|
1545
|
+
columnSpan: 6
|
|
1546
|
+
},
|
|
1547
|
+
{
|
|
1548
|
+
name: "email",
|
|
1549
|
+
type: "email",
|
|
1550
|
+
label: "Email",
|
|
1551
|
+
placeholder: "your@email.com",
|
|
1552
|
+
required: true,
|
|
1553
|
+
columnSpan: 6
|
|
1554
|
+
},
|
|
1555
|
+
{
|
|
1556
|
+
name: "subject",
|
|
1557
|
+
type: "text",
|
|
1558
|
+
label: "Subject",
|
|
1559
|
+
placeholder: "What is this regarding?",
|
|
1560
|
+
required: true,
|
|
1561
|
+
columnSpan: 12
|
|
1562
|
+
},
|
|
1563
|
+
{
|
|
1564
|
+
name: "message",
|
|
1565
|
+
type: "textarea",
|
|
1566
|
+
label: "Message",
|
|
1567
|
+
placeholder: "Your message...",
|
|
1568
|
+
required: true,
|
|
1569
|
+
rows: 4,
|
|
1570
|
+
columnSpan: 12
|
|
1571
|
+
}
|
|
1572
|
+
];
|
|
1153
1573
|
function ContactFaq({
|
|
1154
1574
|
heading,
|
|
1155
1575
|
description,
|
|
1156
1576
|
formHeading,
|
|
1157
|
-
buttonText,
|
|
1577
|
+
buttonText = "Submit",
|
|
1158
1578
|
buttonIcon,
|
|
1159
1579
|
actions,
|
|
1160
1580
|
actionsSlot,
|
|
1161
1581
|
items,
|
|
1162
1582
|
itemsSlot,
|
|
1163
1583
|
faqHeading,
|
|
1584
|
+
formFields = DEFAULT_FORM_FIELDS,
|
|
1585
|
+
successMessage = "Thank you! Your message has been sent successfully.",
|
|
1586
|
+
errorMessage = "There was an error sending your message. Please try again.",
|
|
1164
1587
|
className,
|
|
1588
|
+
containerClassName = "px-6 sm:px-6 md:px-8 lg:px-8",
|
|
1165
1589
|
headerClassName,
|
|
1166
1590
|
headingClassName,
|
|
1167
1591
|
descriptionClassName,
|
|
@@ -1177,9 +1601,10 @@ function ContactFaq({
|
|
|
1177
1601
|
accordionTriggerClassName,
|
|
1178
1602
|
accordionContentClassName,
|
|
1179
1603
|
gridClassName,
|
|
1604
|
+
successMessageClassName,
|
|
1605
|
+
errorMessageClassName,
|
|
1180
1606
|
background,
|
|
1181
1607
|
spacing = "py-8 md:py-32",
|
|
1182
|
-
containerClassName = "px-6 sm:px-6 md:px-8 lg:px-8",
|
|
1183
1608
|
pattern,
|
|
1184
1609
|
patternOpacity,
|
|
1185
1610
|
formConfig,
|
|
@@ -1187,54 +1612,27 @@ function ContactFaq({
|
|
|
1187
1612
|
onSuccess,
|
|
1188
1613
|
onError
|
|
1189
1614
|
}) {
|
|
1190
|
-
const
|
|
1191
|
-
|
|
1192
|
-
|
|
1193
|
-
|
|
1194
|
-
|
|
1195
|
-
|
|
1615
|
+
const {
|
|
1616
|
+
uploadTokens,
|
|
1617
|
+
uploadProgress,
|
|
1618
|
+
isUploading,
|
|
1619
|
+
uploadFiles,
|
|
1620
|
+
removeFile,
|
|
1621
|
+
resetUpload
|
|
1622
|
+
} = useFileUpload({ onError });
|
|
1623
|
+
const { form, isSubmitted, submissionError, formMethod } = useContactForm({
|
|
1624
|
+
formFields,
|
|
1625
|
+
formConfig,
|
|
1626
|
+
onSubmit,
|
|
1627
|
+
onSuccess: (data) => {
|
|
1628
|
+
resetUpload();
|
|
1629
|
+
onSuccess?.(data);
|
|
1196
1630
|
},
|
|
1197
|
-
|
|
1198
|
-
|
|
1199
|
-
|
|
1200
|
-
if (!value) return "Email is required";
|
|
1201
|
-
if (!/^[^\s@]+@[^\s@]+\.[^\s@]+$/.test(value))
|
|
1202
|
-
return "Please enter a valid email address";
|
|
1203
|
-
return void 0;
|
|
1204
|
-
},
|
|
1205
|
-
subject: (value) => !value ? "Subject is required" : void 0,
|
|
1206
|
-
message: (value) => !value ? "Message is required" : void 0
|
|
1207
|
-
},
|
|
1208
|
-
onSubmit: async (values, helpers) => {
|
|
1209
|
-
const shouldAutoSubmit = Boolean(formConfig?.endpoint);
|
|
1210
|
-
if (!shouldAutoSubmit && !onSubmit) {
|
|
1211
|
-
return;
|
|
1212
|
-
}
|
|
1213
|
-
try {
|
|
1214
|
-
let result;
|
|
1215
|
-
if (shouldAutoSubmit) {
|
|
1216
|
-
result = await submitPageSpeedForm(values, formConfig);
|
|
1217
|
-
}
|
|
1218
|
-
if (onSubmit) {
|
|
1219
|
-
await onSubmit(values);
|
|
1220
|
-
}
|
|
1221
|
-
if (shouldAutoSubmit || onSubmit) {
|
|
1222
|
-
if (formConfig?.resetOnSuccess !== false) {
|
|
1223
|
-
helpers.resetForm();
|
|
1224
|
-
}
|
|
1225
|
-
onSuccess?.(result);
|
|
1226
|
-
}
|
|
1227
|
-
} catch (error) {
|
|
1228
|
-
if (error instanceof PageSpeedFormSubmissionError && error.formErrors) {
|
|
1229
|
-
helpers.setErrors(error.formErrors);
|
|
1230
|
-
}
|
|
1231
|
-
onError?.(error);
|
|
1232
|
-
throw error;
|
|
1233
|
-
}
|
|
1234
|
-
}
|
|
1631
|
+
onError,
|
|
1632
|
+
resetOnSuccess: formConfig?.resetOnSuccess !== false,
|
|
1633
|
+
uploadTokens
|
|
1235
1634
|
});
|
|
1236
|
-
const
|
|
1237
|
-
const actionsContent = React__namespace.useMemo(() => {
|
|
1635
|
+
const actionsContent = React.useMemo(() => {
|
|
1238
1636
|
if (actionsSlot) return actionsSlot;
|
|
1239
1637
|
if (actions && actions.length > 0) {
|
|
1240
1638
|
return actions.map((action, index) => {
|
|
@@ -1295,8 +1693,7 @@ function ContactFaq({
|
|
|
1295
1693
|
accordionClassName,
|
|
1296
1694
|
accordionItemClassName,
|
|
1297
1695
|
accordionTriggerClassName,
|
|
1298
|
-
accordionContentClassName
|
|
1299
|
-
background
|
|
1696
|
+
accordionContentClassName
|
|
1300
1697
|
]);
|
|
1301
1698
|
return /* @__PURE__ */ jsxRuntime.jsx(
|
|
1302
1699
|
Section,
|
|
@@ -1359,6 +1756,26 @@ function ContactFaq({
|
|
|
1359
1756
|
children: formHeading
|
|
1360
1757
|
}
|
|
1361
1758
|
) : /* @__PURE__ */ jsxRuntime.jsx("div", { className: formHeadingClassName, children: formHeading })),
|
|
1759
|
+
isSubmitted && /* @__PURE__ */ jsxRuntime.jsx(
|
|
1760
|
+
"div",
|
|
1761
|
+
{
|
|
1762
|
+
className: cn(
|
|
1763
|
+
"mb-6 p-4 bg-primary/10 border border-primary rounded-md",
|
|
1764
|
+
successMessageClassName
|
|
1765
|
+
),
|
|
1766
|
+
children: typeof successMessage === "string" ? /* @__PURE__ */ jsxRuntime.jsx("p", { className: "text-sm text-primary-foreground/90 text-center", children: successMessage }) : successMessage
|
|
1767
|
+
}
|
|
1768
|
+
),
|
|
1769
|
+
submissionError && /* @__PURE__ */ jsxRuntime.jsx(
|
|
1770
|
+
"div",
|
|
1771
|
+
{
|
|
1772
|
+
className: cn(
|
|
1773
|
+
"mb-6 p-4 bg-destructive/10 border border-destructive rounded-md",
|
|
1774
|
+
errorMessageClassName
|
|
1775
|
+
),
|
|
1776
|
+
children: typeof errorMessage === "string" ? /* @__PURE__ */ jsxRuntime.jsx("p", { className: "text-sm text-destructive text-center", children: submissionError }) : errorMessage
|
|
1777
|
+
}
|
|
1778
|
+
),
|
|
1362
1779
|
/* @__PURE__ */ jsxRuntime.jsxs(
|
|
1363
1780
|
forms.Form,
|
|
1364
1781
|
{
|
|
@@ -1367,62 +1784,23 @@ function ContactFaq({
|
|
|
1367
1784
|
method: formMethod,
|
|
1368
1785
|
className: cn("space-y-4", formClassName),
|
|
1369
1786
|
children: [
|
|
1370
|
-
/* @__PURE__ */ jsxRuntime.
|
|
1371
|
-
|
|
1372
|
-
|
|
1373
|
-
|
|
1374
|
-
|
|
1375
|
-
|
|
1376
|
-
...field,
|
|
1377
|
-
id: "name",
|
|
1378
|
-
placeholder: "John Doe",
|
|
1379
|
-
error: meta.touched && !!meta.error,
|
|
1380
|
-
"aria-label": "Name"
|
|
1381
|
-
}
|
|
1382
|
-
)
|
|
1383
|
-
] }) }),
|
|
1384
|
-
/* @__PURE__ */ jsxRuntime.jsx(forms.Field, { name: "email", children: ({ field, meta }) => /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "space-y-2", children: [
|
|
1385
|
-
/* @__PURE__ */ jsxRuntime.jsx(Label, { htmlFor: "email", children: "Email" }),
|
|
1386
|
-
/* @__PURE__ */ jsxRuntime.jsx(
|
|
1387
|
-
inputs.TextInput,
|
|
1787
|
+
/* @__PURE__ */ jsxRuntime.jsx("div", { className: "grid grid-cols-12 gap-4", children: formFields.map((field) => /* @__PURE__ */ jsxRuntime.jsx(
|
|
1788
|
+
"div",
|
|
1789
|
+
{
|
|
1790
|
+
className: getColumnSpanClass(field.columnSpan),
|
|
1791
|
+
children: /* @__PURE__ */ jsxRuntime.jsx(
|
|
1792
|
+
DynamicFormField,
|
|
1388
1793
|
{
|
|
1389
|
-
|
|
1390
|
-
|
|
1391
|
-
|
|
1392
|
-
|
|
1393
|
-
|
|
1394
|
-
"aria-label": "Email"
|
|
1794
|
+
field,
|
|
1795
|
+
uploadProgress,
|
|
1796
|
+
onFileUpload: uploadFiles,
|
|
1797
|
+
onFileRemove: removeFile,
|
|
1798
|
+
isUploading
|
|
1395
1799
|
}
|
|
1396
1800
|
)
|
|
1397
|
-
|
|
1398
|
-
|
|
1399
|
-
|
|
1400
|
-
/* @__PURE__ */ jsxRuntime.jsx(Label, { htmlFor: "subject", children: "Subject" }),
|
|
1401
|
-
/* @__PURE__ */ jsxRuntime.jsx(
|
|
1402
|
-
inputs.TextInput,
|
|
1403
|
-
{
|
|
1404
|
-
...field,
|
|
1405
|
-
id: "subject",
|
|
1406
|
-
placeholder: "What is this regarding?",
|
|
1407
|
-
error: meta.touched && !!meta.error,
|
|
1408
|
-
"aria-label": "Subject"
|
|
1409
|
-
}
|
|
1410
|
-
)
|
|
1411
|
-
] }) }),
|
|
1412
|
-
/* @__PURE__ */ jsxRuntime.jsx(forms.Field, { name: "message", children: ({ field, meta }) => /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "space-y-2", children: [
|
|
1413
|
-
/* @__PURE__ */ jsxRuntime.jsx(Label, { htmlFor: "message", children: "Message" }),
|
|
1414
|
-
/* @__PURE__ */ jsxRuntime.jsx(
|
|
1415
|
-
inputs.TextArea,
|
|
1416
|
-
{
|
|
1417
|
-
...field,
|
|
1418
|
-
id: "message",
|
|
1419
|
-
placeholder: "Your question...",
|
|
1420
|
-
rows: 4,
|
|
1421
|
-
error: meta.touched && !!meta.error,
|
|
1422
|
-
"aria-label": "Message"
|
|
1423
|
-
}
|
|
1424
|
-
)
|
|
1425
|
-
] }) }),
|
|
1801
|
+
},
|
|
1802
|
+
field.name
|
|
1803
|
+
)) }),
|
|
1426
1804
|
actionsSlot || actions && actions.length > 0 ? actionsContent : /* @__PURE__ */ jsxRuntime.jsxs(
|
|
1427
1805
|
Pressable,
|
|
1428
1806
|
{
|