@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.
@@ -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 integration = require('@page-speed/forms/integration');
13
13
 
@@ -588,6 +588,389 @@ function Label({
588
588
  }
589
589
  );
590
590
  }
591
+ function DynamicFormField({
592
+ field,
593
+ className,
594
+ uploadProgress = {},
595
+ onFileUpload,
596
+ onFileRemove,
597
+ isUploading = false
598
+ }) {
599
+ const fieldId = `field-${field.name}`;
600
+ return /* @__PURE__ */ jsxRuntime.jsx(forms.Field, { name: field.name, children: ({ field: formField, meta }) => /* @__PURE__ */ jsxRuntime.jsxs("div", { className: cn("space-y-2", className), children: [
601
+ field.type !== "checkbox" && /* @__PURE__ */ jsxRuntime.jsxs(Label, { htmlFor: fieldId, children: [
602
+ field.label,
603
+ field.required && /* @__PURE__ */ jsxRuntime.jsx("span", { className: "text-destructive ml-1", children: "*" })
604
+ ] }),
605
+ (field.type === "text" || field.type === "email" || field.type === "tel" || field.type === "search" || field.type === "password" || field.type === "url") && /* @__PURE__ */ jsxRuntime.jsx(
606
+ inputs.TextInput,
607
+ {
608
+ ...formField,
609
+ id: fieldId,
610
+ type: field.type,
611
+ placeholder: field.placeholder,
612
+ error: meta.touched && !!meta.error,
613
+ disabled: field.disabled,
614
+ "aria-label": field.label
615
+ }
616
+ ),
617
+ field.type === "number" && /* @__PURE__ */ jsxRuntime.jsx(
618
+ inputs.TextInput,
619
+ {
620
+ ...formField,
621
+ id: fieldId,
622
+ type: "text",
623
+ placeholder: field.placeholder,
624
+ error: meta.touched && !!meta.error,
625
+ disabled: field.disabled,
626
+ "aria-label": field.label
627
+ }
628
+ ),
629
+ field.type === "textarea" && /* @__PURE__ */ jsxRuntime.jsx(
630
+ inputs.TextArea,
631
+ {
632
+ ...formField,
633
+ id: fieldId,
634
+ placeholder: field.placeholder,
635
+ rows: field.rows || 4,
636
+ error: meta.touched && !!meta.error,
637
+ disabled: field.disabled,
638
+ "aria-label": field.label
639
+ }
640
+ ),
641
+ field.type === "select" && field.options && /* @__PURE__ */ jsxRuntime.jsx(
642
+ inputs.Select,
643
+ {
644
+ ...formField,
645
+ id: fieldId,
646
+ options: field.options,
647
+ placeholder: field.placeholder || `Select ${field.label.toLowerCase()}`,
648
+ error: meta.touched && !!meta.error,
649
+ disabled: field.disabled,
650
+ "aria-label": field.label
651
+ }
652
+ ),
653
+ field.type === "multi-select" && field.options && /* @__PURE__ */ jsxRuntime.jsx(
654
+ inputs.Select,
655
+ {
656
+ ...formField,
657
+ id: fieldId,
658
+ options: field.options,
659
+ placeholder: field.placeholder || `Select ${field.label.toLowerCase()}`,
660
+ error: meta.touched && !!meta.error,
661
+ disabled: field.disabled,
662
+ "aria-label": field.label,
663
+ multiple: true
664
+ }
665
+ ),
666
+ field.type === "radio" && field.options && /* @__PURE__ */ jsxRuntime.jsx(
667
+ inputs.Radio,
668
+ {
669
+ ...formField,
670
+ id: fieldId,
671
+ options: field.options,
672
+ disabled: field.disabled,
673
+ layout: field.layout || "stacked",
674
+ error: meta.touched && !!meta.error,
675
+ "aria-label": field.label
676
+ }
677
+ ),
678
+ field.type === "checkbox" && /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "flex items-start space-x-2", children: [
679
+ /* @__PURE__ */ jsxRuntime.jsx(
680
+ inputs.Checkbox,
681
+ {
682
+ ...formField,
683
+ id: fieldId,
684
+ value: formField.value === true || formField.value === "true",
685
+ onChange: (checked) => formField.onChange(checked),
686
+ disabled: field.disabled,
687
+ error: meta.touched && !!meta.error,
688
+ "aria-label": field.label
689
+ }
690
+ ),
691
+ /* @__PURE__ */ jsxRuntime.jsxs(
692
+ Label,
693
+ {
694
+ htmlFor: fieldId,
695
+ className: "font-normal cursor-pointer leading-relaxed",
696
+ children: [
697
+ field.label,
698
+ field.required && /* @__PURE__ */ jsxRuntime.jsx("span", { className: "text-destructive ml-1", children: "*" })
699
+ ]
700
+ }
701
+ )
702
+ ] }),
703
+ field.type === "checkbox-group" && field.options && /* @__PURE__ */ jsxRuntime.jsx(
704
+ inputs.CheckboxGroup,
705
+ {
706
+ ...formField,
707
+ id: fieldId,
708
+ options: field.options,
709
+ disabled: field.disabled,
710
+ layout: field.layout || "stacked",
711
+ error: meta.touched && !!meta.error,
712
+ "aria-label": field.label
713
+ }
714
+ ),
715
+ (field.type === "date-picker" || field.type === "date") && /* @__PURE__ */ jsxRuntime.jsx(
716
+ inputs.DatePicker,
717
+ {
718
+ ...formField,
719
+ id: fieldId,
720
+ placeholder: field.placeholder,
721
+ error: meta.touched && !!meta.error,
722
+ disabled: field.disabled,
723
+ "aria-label": field.label
724
+ }
725
+ ),
726
+ field.type === "date-range" && /* @__PURE__ */ jsxRuntime.jsx(
727
+ inputs.DateRangePicker,
728
+ {
729
+ ...formField,
730
+ id: fieldId,
731
+ error: meta.touched && !!meta.error,
732
+ disabled: field.disabled,
733
+ "aria-label": field.label
734
+ }
735
+ ),
736
+ field.type === "time" && /* @__PURE__ */ jsxRuntime.jsx(
737
+ inputs.TimePicker,
738
+ {
739
+ ...formField,
740
+ id: fieldId,
741
+ placeholder: field.placeholder,
742
+ error: meta.touched && !!meta.error,
743
+ disabled: field.disabled,
744
+ "aria-label": field.label
745
+ }
746
+ ),
747
+ field.type === "file" && /* @__PURE__ */ jsxRuntime.jsx(
748
+ inputs.FileInput,
749
+ {
750
+ ...formField,
751
+ id: fieldId,
752
+ accept: field.accept,
753
+ maxSize: field.maxSize || 5 * 1024 * 1024,
754
+ maxFiles: field.maxFiles || 1,
755
+ multiple: field.multiple || false,
756
+ placeholder: field.placeholder || "Choose file(s)...",
757
+ error: meta.touched && !!meta.error,
758
+ disabled: field.disabled || isUploading,
759
+ showProgress: true,
760
+ uploadProgress,
761
+ onChange: (files) => {
762
+ formField.onChange(files);
763
+ if (files.length > 0 && onFileUpload) {
764
+ onFileUpload(files);
765
+ }
766
+ },
767
+ onFileRemove,
768
+ "aria-label": field.label
769
+ }
770
+ ),
771
+ field.type === "rich-text" && /* @__PURE__ */ jsxRuntime.jsx(
772
+ inputs.RichTextEditor,
773
+ {
774
+ ...formField,
775
+ id: fieldId,
776
+ placeholder: field.placeholder,
777
+ error: meta.touched && !!meta.error,
778
+ disabled: field.disabled,
779
+ "aria-label": field.label
780
+ }
781
+ ),
782
+ meta.touched && meta.error && /* @__PURE__ */ jsxRuntime.jsx("p", { className: "text-sm text-destructive", children: meta.error })
783
+ ] }) });
784
+ }
785
+
786
+ // lib/form-field-types.ts
787
+ function generateInitialValues(fields) {
788
+ return fields.reduce(
789
+ (acc, field) => {
790
+ if (field.type === "checkbox") {
791
+ acc[field.name] = false;
792
+ } else if (field.type === "checkbox-group" || field.type === "multi-select") {
793
+ acc[field.name] = [];
794
+ } else if (field.type === "file") {
795
+ acc[field.name] = [];
796
+ } else if (field.type === "date-range") {
797
+ acc[field.name] = { start: null, end: null };
798
+ } else {
799
+ acc[field.name] = "";
800
+ }
801
+ return acc;
802
+ },
803
+ {}
804
+ );
805
+ }
806
+ function generateValidationSchema(fields) {
807
+ return fields.reduce(
808
+ (acc, field) => {
809
+ acc[field.name] = (value, allValues) => {
810
+ if (field.required) {
811
+ if (!value || typeof value === "string" && !value.trim()) {
812
+ return `${field.label} is required`;
813
+ }
814
+ }
815
+ if (field.type === "email" && value) {
816
+ if (!/^[^\s@]+@[^\s@]+\.[^\s@]+$/.test(value)) {
817
+ return "Please enter a valid email address";
818
+ }
819
+ }
820
+ if (field.type === "url" && value) {
821
+ try {
822
+ new URL(value);
823
+ } catch {
824
+ return "Please enter a valid URL";
825
+ }
826
+ }
827
+ if (field.validator) {
828
+ return field.validator(value, allValues);
829
+ }
830
+ return void 0;
831
+ };
832
+ return acc;
833
+ },
834
+ {}
835
+ );
836
+ }
837
+ function getColumnSpanClass(span) {
838
+ if (!span || span === 12) return "col-span-12";
839
+ return `col-span-12 sm:col-span-${Math.min(span, 12)}`;
840
+ }
841
+ function useFileUpload(options) {
842
+ const [uploadTokens, setUploadTokens] = React.useState([]);
843
+ const [uploadProgress, setUploadProgress] = React.useState({});
844
+ const [isUploading, setIsUploading] = React.useState(false);
845
+ const endpoint = options?.endpoint || "https://api.dashtrack.com/contacts/_/contact_form_uploads";
846
+ const uploadFiles = React.useCallback(
847
+ async (files) => {
848
+ if (files.length === 0) return;
849
+ setIsUploading(true);
850
+ try {
851
+ const tokens = [];
852
+ for (const file of files) {
853
+ const formData = new FormData();
854
+ formData.append("contact_form_upload[file_upload]", file);
855
+ formData.append("contact_form_upload[title]", file.name);
856
+ formData.append("contact_form_upload[file_name]", file.name);
857
+ formData.append("contact_form_upload[file_size]", String(file.size));
858
+ const response = await fetch(endpoint, {
859
+ method: "POST",
860
+ body: formData
861
+ });
862
+ if (!response.ok) {
863
+ throw new Error(`Upload failed: ${response.statusText}`);
864
+ }
865
+ const data = await response.json();
866
+ if (data.contact_form_upload?.token) {
867
+ tokens.push(`upload_${data.contact_form_upload.token}`);
868
+ }
869
+ setUploadProgress((prev) => ({
870
+ ...prev,
871
+ [file.name]: 100
872
+ }));
873
+ }
874
+ setUploadTokens(tokens);
875
+ } catch (error) {
876
+ console.error("File upload error:", error);
877
+ options?.onError?.(error);
878
+ } finally {
879
+ setIsUploading(false);
880
+ }
881
+ },
882
+ [endpoint, options]
883
+ );
884
+ const removeFile = React.useCallback((file, index) => {
885
+ setUploadTokens((prev) => prev.filter((_, i) => i !== index));
886
+ setUploadProgress((prev) => {
887
+ const newProgress = { ...prev };
888
+ delete newProgress[file.name];
889
+ return newProgress;
890
+ });
891
+ }, []);
892
+ const resetUpload = React.useCallback(() => {
893
+ setUploadTokens([]);
894
+ setUploadProgress({});
895
+ }, []);
896
+ return {
897
+ uploadTokens,
898
+ uploadProgress,
899
+ isUploading,
900
+ uploadFiles,
901
+ removeFile,
902
+ resetUpload
903
+ };
904
+ }
905
+ function useContactForm(options) {
906
+ const {
907
+ formFields,
908
+ formConfig,
909
+ onSubmit,
910
+ onSuccess,
911
+ onError,
912
+ resetOnSuccess = true,
913
+ uploadTokens = []
914
+ } = options;
915
+ const [isSubmitted, setIsSubmitted] = React.useState(false);
916
+ const [submissionError, setSubmissionError] = React.useState(null);
917
+ const form = forms.useForm({
918
+ initialValues: React.useMemo(
919
+ () => generateInitialValues(formFields),
920
+ [formFields]
921
+ ),
922
+ validationSchema: React.useMemo(
923
+ () => generateValidationSchema(formFields),
924
+ [formFields]
925
+ ),
926
+ onSubmit: async (values, helpers) => {
927
+ setSubmissionError(null);
928
+ const shouldAutoSubmit = Boolean(formConfig?.endpoint);
929
+ if (!shouldAutoSubmit && !onSubmit) {
930
+ return;
931
+ }
932
+ try {
933
+ let result;
934
+ const submissionValues = {
935
+ ...values,
936
+ ...uploadTokens.length > 0 && {
937
+ contact_form_upload_tokens: uploadTokens
938
+ }
939
+ };
940
+ if (shouldAutoSubmit) {
941
+ result = await submitPageSpeedForm(submissionValues, formConfig);
942
+ }
943
+ if (onSubmit) {
944
+ await onSubmit(submissionValues);
945
+ }
946
+ if (shouldAutoSubmit || onSubmit) {
947
+ setIsSubmitted(true);
948
+ if (resetOnSuccess) {
949
+ helpers.resetForm();
950
+ }
951
+ onSuccess?.(result);
952
+ setTimeout(() => setIsSubmitted(false), 5e3);
953
+ }
954
+ } catch (error) {
955
+ if (error instanceof PageSpeedFormSubmissionError && error.formErrors) {
956
+ helpers.setErrors(error.formErrors);
957
+ }
958
+ const errorMessage = error instanceof Error ? error.message : "Form submission failed";
959
+ setSubmissionError(errorMessage);
960
+ onError?.(error);
961
+ }
962
+ }
963
+ });
964
+ const formMethod = formConfig?.method?.toLowerCase() === "get" ? "get" : "post";
965
+ return {
966
+ form,
967
+ isSubmitted,
968
+ submissionError,
969
+ formMethod
970
+ };
971
+ }
972
+
973
+ // lib/forms.ts
591
974
  var PageSpeedFormSubmissionError = class extends Error {
592
975
  constructor(message, options = {}) {
593
976
  super(message);
@@ -1078,12 +1461,47 @@ var Section = React__namespace.default.forwardRef(
1078
1461
  }
1079
1462
  );
1080
1463
  Section.displayName = "Section";
1464
+ var DEFAULT_FORM_FIELDS = [
1465
+ {
1466
+ name: "firstName",
1467
+ type: "text",
1468
+ label: "First Name",
1469
+ placeholder: "First name",
1470
+ required: true,
1471
+ columnSpan: 6
1472
+ },
1473
+ {
1474
+ name: "lastName",
1475
+ type: "text",
1476
+ label: "Last Name",
1477
+ placeholder: "Last name",
1478
+ required: true,
1479
+ columnSpan: 6
1480
+ },
1481
+ {
1482
+ name: "email",
1483
+ type: "email",
1484
+ label: "Email Address",
1485
+ placeholder: "your@email.com",
1486
+ required: true,
1487
+ columnSpan: 12
1488
+ },
1489
+ {
1490
+ name: "message",
1491
+ type: "textarea",
1492
+ label: "Message",
1493
+ placeholder: "Your message...",
1494
+ required: true,
1495
+ rows: 4,
1496
+ columnSpan: 12
1497
+ }
1498
+ ];
1081
1499
  function ContactDark({
1082
1500
  heading,
1083
1501
  description,
1084
1502
  contactHeading = "Contact Information",
1085
1503
  contactDescription = "Fill up the form and our team will get back to you within 24 hours.",
1086
- buttonText,
1504
+ buttonText = "Submit",
1087
1505
  buttonIcon,
1088
1506
  actions,
1089
1507
  actionsSlot,
@@ -1091,6 +1509,9 @@ function ContactDark({
1091
1509
  contactOptionsSlot,
1092
1510
  socialLinks,
1093
1511
  socialLinksSlot,
1512
+ formFields = DEFAULT_FORM_FIELDS,
1513
+ successMessage = "Thank you! Your message has been sent successfully.",
1514
+ errorMessage = "There was an error sending your message. Please try again.",
1094
1515
  className,
1095
1516
  headerClassName,
1096
1517
  headingClassName,
@@ -1102,6 +1523,8 @@ function ContactDark({
1102
1523
  infoPanelClassName,
1103
1524
  contactOptionsClassName,
1104
1525
  socialLinksClassName,
1526
+ successMessageClassName,
1527
+ errorMessageClassName,
1105
1528
  background,
1106
1529
  spacing = "py-8 md:py-32",
1107
1530
  containerClassName = "px-6 sm:px-6 md:px-8 lg:px-8",
@@ -1112,53 +1535,26 @@ function ContactDark({
1112
1535
  onSuccess,
1113
1536
  onError
1114
1537
  }) {
1115
- const form = forms.useForm({
1116
- initialValues: {
1117
- firstName: "",
1118
- lastName: "",
1119
- email: "",
1120
- message: ""
1121
- },
1122
- validationSchema: {
1123
- firstName: (value) => !value ? "First name is required" : void 0,
1124
- lastName: (value) => !value ? "Last name is required" : void 0,
1125
- email: (value) => {
1126
- if (!value) return "Email is required";
1127
- if (!/^[^\s@]+@[^\s@]+\.[^\s@]+$/.test(value))
1128
- return "Please enter a valid email address";
1129
- return void 0;
1130
- },
1131
- message: (value) => !value ? "Message is required" : void 0
1538
+ const {
1539
+ uploadTokens,
1540
+ uploadProgress,
1541
+ isUploading,
1542
+ uploadFiles,
1543
+ removeFile,
1544
+ resetUpload
1545
+ } = useFileUpload({ onError });
1546
+ const { form, isSubmitted, submissionError, formMethod } = useContactForm({
1547
+ formFields,
1548
+ formConfig,
1549
+ onSubmit,
1550
+ onSuccess: (data) => {
1551
+ resetUpload();
1552
+ onSuccess?.(data);
1132
1553
  },
1133
- onSubmit: async (values, helpers) => {
1134
- const shouldAutoSubmit = Boolean(formConfig?.endpoint);
1135
- if (!shouldAutoSubmit && !onSubmit) {
1136
- return;
1137
- }
1138
- try {
1139
- let result;
1140
- if (shouldAutoSubmit) {
1141
- result = await submitPageSpeedForm(values, formConfig);
1142
- }
1143
- if (onSubmit) {
1144
- await onSubmit(values);
1145
- }
1146
- if (shouldAutoSubmit || onSubmit) {
1147
- if (formConfig?.resetOnSuccess !== false) {
1148
- helpers.resetForm();
1149
- }
1150
- onSuccess?.(result);
1151
- }
1152
- } catch (error) {
1153
- if (error instanceof PageSpeedFormSubmissionError && error.formErrors) {
1154
- helpers.setErrors(error.formErrors);
1155
- }
1156
- onError?.(error);
1157
- throw error;
1158
- }
1159
- }
1554
+ onError,
1555
+ resetOnSuccess: formConfig?.resetOnSuccess !== false,
1556
+ uploadTokens
1160
1557
  });
1161
- const formMethod = formConfig?.method?.toLowerCase() === "get" ? "get" : "post";
1162
1558
  const actionsContent = React__namespace.useMemo(() => {
1163
1559
  if (actionsSlot) return actionsSlot;
1164
1560
  if (actions && actions.length > 0) {
@@ -1269,87 +1665,70 @@ function ContactDark({
1269
1665
  cardClassName
1270
1666
  ),
1271
1667
  children: [
1272
- /* @__PURE__ */ jsxRuntime.jsx("div", { className: cn("p-6 lg:p-12", formPanelClassName), children: /* @__PURE__ */ jsxRuntime.jsxs(
1273
- forms.Form,
1274
- {
1275
- form,
1276
- action: formConfig?.endpoint,
1277
- method: formMethod,
1278
- className: cn("space-y-6", formClassName),
1279
- children: [
1280
- /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "grid gap-4 sm:grid-cols-2", children: [
1281
- /* @__PURE__ */ jsxRuntime.jsx(forms.Field, { name: "firstName", children: ({ field, meta }) => /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "space-y-2", children: [
1282
- /* @__PURE__ */ jsxRuntime.jsx(Label, { htmlFor: "first-name", children: "First Name" }),
1283
- /* @__PURE__ */ jsxRuntime.jsx(
1284
- inputs.TextInput,
1285
- {
1286
- ...field,
1287
- id: "first-name",
1288
- placeholder: "First name",
1289
- error: meta.touched && !!meta.error,
1290
- "aria-label": "First Name"
1291
- }
1292
- )
1293
- ] }) }),
1294
- /* @__PURE__ */ jsxRuntime.jsx(forms.Field, { name: "lastName", children: ({ field, meta }) => /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "space-y-2", children: [
1295
- /* @__PURE__ */ jsxRuntime.jsx(Label, { htmlFor: "last-name", children: "Last Name" }),
1296
- /* @__PURE__ */ jsxRuntime.jsx(
1297
- inputs.TextInput,
1298
- {
1299
- ...field,
1300
- id: "last-name",
1301
- placeholder: "Last name",
1302
- error: meta.touched && !!meta.error,
1303
- "aria-label": "Last Name"
1304
- }
1305
- )
1306
- ] }) })
1307
- ] }),
1308
- /* @__PURE__ */ jsxRuntime.jsx(forms.Field, { name: "email", children: ({ field, meta }) => /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "space-y-2", children: [
1309
- /* @__PURE__ */ jsxRuntime.jsx(Label, { htmlFor: "email", children: "Email Address" }),
1310
- /* @__PURE__ */ jsxRuntime.jsx(
1311
- inputs.TextInput,
1668
+ /* @__PURE__ */ jsxRuntime.jsxs("div", { className: cn("p-6 lg:p-12", formPanelClassName), children: [
1669
+ isSubmitted && /* @__PURE__ */ jsxRuntime.jsx(
1670
+ "div",
1671
+ {
1672
+ className: cn(
1673
+ "mb-6 p-4 bg-primary/10 border border-primary rounded-md",
1674
+ successMessageClassName
1675
+ ),
1676
+ children: typeof successMessage === "string" ? /* @__PURE__ */ jsxRuntime.jsx("p", { className: "text-sm text-primary-foreground/90 text-center", children: successMessage }) : successMessage
1677
+ }
1678
+ ),
1679
+ submissionError && /* @__PURE__ */ jsxRuntime.jsx(
1680
+ "div",
1681
+ {
1682
+ className: cn(
1683
+ "mb-6 p-4 bg-destructive/10 border border-destructive rounded-md",
1684
+ errorMessageClassName
1685
+ ),
1686
+ children: /* @__PURE__ */ jsxRuntime.jsx("p", { className: "text-sm text-destructive text-center", children: submissionError })
1687
+ }
1688
+ ),
1689
+ /* @__PURE__ */ jsxRuntime.jsxs(
1690
+ forms.Form,
1691
+ {
1692
+ form,
1693
+ action: formConfig?.endpoint,
1694
+ method: formMethod,
1695
+ className: cn("space-y-6", formClassName),
1696
+ children: [
1697
+ /* @__PURE__ */ jsxRuntime.jsx("div", { className: "grid grid-cols-12 gap-4", children: formFields.map((field) => /* @__PURE__ */ jsxRuntime.jsx(
1698
+ "div",
1312
1699
  {
1313
- ...field,
1314
- id: "email",
1315
- type: "email",
1316
- placeholder: "Your email",
1317
- error: meta.touched && !!meta.error,
1318
- "aria-label": "Email Address"
1319
- }
1320
- )
1321
- ] }) }),
1322
- /* @__PURE__ */ jsxRuntime.jsx(forms.Field, { name: "message", children: ({ field, meta }) => /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "space-y-2", children: [
1323
- /* @__PURE__ */ jsxRuntime.jsx(Label, { htmlFor: "message", children: "Message" }),
1324
- /* @__PURE__ */ jsxRuntime.jsx(
1325
- inputs.TextArea,
1700
+ className: getColumnSpanClass(field.columnSpan),
1701
+ children: /* @__PURE__ */ jsxRuntime.jsx(
1702
+ DynamicFormField,
1703
+ {
1704
+ field,
1705
+ uploadProgress,
1706
+ onFileUpload: uploadFiles,
1707
+ onFileRemove: removeFile,
1708
+ isUploading
1709
+ }
1710
+ )
1711
+ },
1712
+ field.name
1713
+ )) }),
1714
+ actionsSlot || actions && actions.length > 0 ? actionsContent : /* @__PURE__ */ jsxRuntime.jsxs(
1715
+ Pressable,
1326
1716
  {
1327
- ...field,
1328
- id: "message",
1329
- placeholder: "Your message...",
1330
- rows: 4,
1331
- error: meta.touched && !!meta.error,
1332
- "aria-label": "Message"
1717
+ componentType: "button",
1718
+ type: "submit",
1719
+ className: cn("w-full", submitClassName),
1720
+ asButton: true,
1721
+ disabled: form.isSubmitting,
1722
+ children: [
1723
+ buttonIcon,
1724
+ buttonText
1725
+ ]
1333
1726
  }
1334
1727
  )
1335
- ] }) }),
1336
- actionsSlot || actions && actions.length > 0 ? actionsContent : /* @__PURE__ */ jsxRuntime.jsxs(
1337
- Pressable,
1338
- {
1339
- componentType: "button",
1340
- type: "submit",
1341
- className: cn("w-full", submitClassName),
1342
- asButton: true,
1343
- disabled: form.isSubmitting,
1344
- children: [
1345
- buttonIcon,
1346
- buttonText
1347
- ]
1348
- }
1349
- )
1350
- ]
1351
- }
1352
- ) }),
1728
+ ]
1729
+ }
1730
+ )
1731
+ ] }),
1353
1732
  /* @__PURE__ */ jsxRuntime.jsxs(
1354
1733
  "div",
1355
1734
  {