@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.
@@ -1,12 +1,12 @@
1
1
  "use client";
2
2
  import * as React from 'react';
3
- import React__default from 'react';
4
- import { useForm, Form, Field } from '@page-speed/forms';
5
- import { TextInput, TextArea } from '@page-speed/forms/inputs';
3
+ import React__default, { useState, useCallback, useMemo } from 'react';
4
+ import { Form, useForm, Field } from '@page-speed/forms';
6
5
  import { clsx } from 'clsx';
7
6
  import { twMerge } from 'tailwind-merge';
8
7
  import { cva } from 'class-variance-authority';
9
8
  import { jsx, jsxs, Fragment } from 'react/jsx-runtime';
9
+ import { TextInput, TextArea, Select, Radio, Checkbox, CheckboxGroup, DatePicker, DateRangePicker, TimePicker, FileInput, RichTextEditor } from '@page-speed/forms/inputs';
10
10
  import * as LabelPrimitive from '@radix-ui/react-label';
11
11
  import { serializeForRails, deserializeErrors } from '@page-speed/forms/integration';
12
12
 
@@ -566,6 +566,389 @@ function Label({
566
566
  }
567
567
  );
568
568
  }
569
+ function DynamicFormField({
570
+ field,
571
+ className,
572
+ uploadProgress = {},
573
+ onFileUpload,
574
+ onFileRemove,
575
+ isUploading = false
576
+ }) {
577
+ const fieldId = `field-${field.name}`;
578
+ return /* @__PURE__ */ jsx(Field, { name: field.name, children: ({ field: formField, meta }) => /* @__PURE__ */ jsxs("div", { className: cn("space-y-2", className), children: [
579
+ field.type !== "checkbox" && /* @__PURE__ */ jsxs(Label, { htmlFor: fieldId, children: [
580
+ field.label,
581
+ field.required && /* @__PURE__ */ jsx("span", { className: "text-destructive ml-1", children: "*" })
582
+ ] }),
583
+ (field.type === "text" || field.type === "email" || field.type === "tel" || field.type === "search" || field.type === "password" || field.type === "url") && /* @__PURE__ */ jsx(
584
+ TextInput,
585
+ {
586
+ ...formField,
587
+ id: fieldId,
588
+ type: field.type,
589
+ placeholder: field.placeholder,
590
+ error: meta.touched && !!meta.error,
591
+ disabled: field.disabled,
592
+ "aria-label": field.label
593
+ }
594
+ ),
595
+ field.type === "number" && /* @__PURE__ */ jsx(
596
+ TextInput,
597
+ {
598
+ ...formField,
599
+ id: fieldId,
600
+ type: "text",
601
+ placeholder: field.placeholder,
602
+ error: meta.touched && !!meta.error,
603
+ disabled: field.disabled,
604
+ "aria-label": field.label
605
+ }
606
+ ),
607
+ field.type === "textarea" && /* @__PURE__ */ jsx(
608
+ TextArea,
609
+ {
610
+ ...formField,
611
+ id: fieldId,
612
+ placeholder: field.placeholder,
613
+ rows: field.rows || 4,
614
+ error: meta.touched && !!meta.error,
615
+ disabled: field.disabled,
616
+ "aria-label": field.label
617
+ }
618
+ ),
619
+ field.type === "select" && field.options && /* @__PURE__ */ jsx(
620
+ Select,
621
+ {
622
+ ...formField,
623
+ id: fieldId,
624
+ options: field.options,
625
+ placeholder: field.placeholder || `Select ${field.label.toLowerCase()}`,
626
+ error: meta.touched && !!meta.error,
627
+ disabled: field.disabled,
628
+ "aria-label": field.label
629
+ }
630
+ ),
631
+ field.type === "multi-select" && field.options && /* @__PURE__ */ jsx(
632
+ Select,
633
+ {
634
+ ...formField,
635
+ id: fieldId,
636
+ options: field.options,
637
+ placeholder: field.placeholder || `Select ${field.label.toLowerCase()}`,
638
+ error: meta.touched && !!meta.error,
639
+ disabled: field.disabled,
640
+ "aria-label": field.label,
641
+ multiple: true
642
+ }
643
+ ),
644
+ field.type === "radio" && field.options && /* @__PURE__ */ jsx(
645
+ Radio,
646
+ {
647
+ ...formField,
648
+ id: fieldId,
649
+ options: field.options,
650
+ disabled: field.disabled,
651
+ layout: field.layout || "stacked",
652
+ error: meta.touched && !!meta.error,
653
+ "aria-label": field.label
654
+ }
655
+ ),
656
+ field.type === "checkbox" && /* @__PURE__ */ jsxs("div", { className: "flex items-start space-x-2", children: [
657
+ /* @__PURE__ */ jsx(
658
+ Checkbox,
659
+ {
660
+ ...formField,
661
+ id: fieldId,
662
+ value: formField.value === true || formField.value === "true",
663
+ onChange: (checked) => formField.onChange(checked),
664
+ disabled: field.disabled,
665
+ error: meta.touched && !!meta.error,
666
+ "aria-label": field.label
667
+ }
668
+ ),
669
+ /* @__PURE__ */ jsxs(
670
+ Label,
671
+ {
672
+ htmlFor: fieldId,
673
+ className: "font-normal cursor-pointer leading-relaxed",
674
+ children: [
675
+ field.label,
676
+ field.required && /* @__PURE__ */ jsx("span", { className: "text-destructive ml-1", children: "*" })
677
+ ]
678
+ }
679
+ )
680
+ ] }),
681
+ field.type === "checkbox-group" && field.options && /* @__PURE__ */ jsx(
682
+ CheckboxGroup,
683
+ {
684
+ ...formField,
685
+ id: fieldId,
686
+ options: field.options,
687
+ disabled: field.disabled,
688
+ layout: field.layout || "stacked",
689
+ error: meta.touched && !!meta.error,
690
+ "aria-label": field.label
691
+ }
692
+ ),
693
+ (field.type === "date-picker" || field.type === "date") && /* @__PURE__ */ jsx(
694
+ DatePicker,
695
+ {
696
+ ...formField,
697
+ id: fieldId,
698
+ placeholder: field.placeholder,
699
+ error: meta.touched && !!meta.error,
700
+ disabled: field.disabled,
701
+ "aria-label": field.label
702
+ }
703
+ ),
704
+ field.type === "date-range" && /* @__PURE__ */ jsx(
705
+ DateRangePicker,
706
+ {
707
+ ...formField,
708
+ id: fieldId,
709
+ error: meta.touched && !!meta.error,
710
+ disabled: field.disabled,
711
+ "aria-label": field.label
712
+ }
713
+ ),
714
+ field.type === "time" && /* @__PURE__ */ jsx(
715
+ TimePicker,
716
+ {
717
+ ...formField,
718
+ id: fieldId,
719
+ placeholder: field.placeholder,
720
+ error: meta.touched && !!meta.error,
721
+ disabled: field.disabled,
722
+ "aria-label": field.label
723
+ }
724
+ ),
725
+ field.type === "file" && /* @__PURE__ */ jsx(
726
+ FileInput,
727
+ {
728
+ ...formField,
729
+ id: fieldId,
730
+ accept: field.accept,
731
+ maxSize: field.maxSize || 5 * 1024 * 1024,
732
+ maxFiles: field.maxFiles || 1,
733
+ multiple: field.multiple || false,
734
+ placeholder: field.placeholder || "Choose file(s)...",
735
+ error: meta.touched && !!meta.error,
736
+ disabled: field.disabled || isUploading,
737
+ showProgress: true,
738
+ uploadProgress,
739
+ onChange: (files) => {
740
+ formField.onChange(files);
741
+ if (files.length > 0 && onFileUpload) {
742
+ onFileUpload(files);
743
+ }
744
+ },
745
+ onFileRemove,
746
+ "aria-label": field.label
747
+ }
748
+ ),
749
+ field.type === "rich-text" && /* @__PURE__ */ jsx(
750
+ RichTextEditor,
751
+ {
752
+ ...formField,
753
+ id: fieldId,
754
+ placeholder: field.placeholder,
755
+ error: meta.touched && !!meta.error,
756
+ disabled: field.disabled,
757
+ "aria-label": field.label
758
+ }
759
+ ),
760
+ meta.touched && meta.error && /* @__PURE__ */ jsx("p", { className: "text-sm text-destructive", children: meta.error })
761
+ ] }) });
762
+ }
763
+
764
+ // lib/form-field-types.ts
765
+ function generateInitialValues(fields) {
766
+ return fields.reduce(
767
+ (acc, field) => {
768
+ if (field.type === "checkbox") {
769
+ acc[field.name] = false;
770
+ } else if (field.type === "checkbox-group" || field.type === "multi-select") {
771
+ acc[field.name] = [];
772
+ } else if (field.type === "file") {
773
+ acc[field.name] = [];
774
+ } else if (field.type === "date-range") {
775
+ acc[field.name] = { start: null, end: null };
776
+ } else {
777
+ acc[field.name] = "";
778
+ }
779
+ return acc;
780
+ },
781
+ {}
782
+ );
783
+ }
784
+ function generateValidationSchema(fields) {
785
+ return fields.reduce(
786
+ (acc, field) => {
787
+ acc[field.name] = (value, allValues) => {
788
+ if (field.required) {
789
+ if (!value || typeof value === "string" && !value.trim()) {
790
+ return `${field.label} is required`;
791
+ }
792
+ }
793
+ if (field.type === "email" && value) {
794
+ if (!/^[^\s@]+@[^\s@]+\.[^\s@]+$/.test(value)) {
795
+ return "Please enter a valid email address";
796
+ }
797
+ }
798
+ if (field.type === "url" && value) {
799
+ try {
800
+ new URL(value);
801
+ } catch {
802
+ return "Please enter a valid URL";
803
+ }
804
+ }
805
+ if (field.validator) {
806
+ return field.validator(value, allValues);
807
+ }
808
+ return void 0;
809
+ };
810
+ return acc;
811
+ },
812
+ {}
813
+ );
814
+ }
815
+ function getColumnSpanClass(span) {
816
+ if (!span || span === 12) return "col-span-12";
817
+ return `col-span-12 sm:col-span-${Math.min(span, 12)}`;
818
+ }
819
+ function useFileUpload(options) {
820
+ const [uploadTokens, setUploadTokens] = useState([]);
821
+ const [uploadProgress, setUploadProgress] = useState({});
822
+ const [isUploading, setIsUploading] = useState(false);
823
+ const endpoint = options?.endpoint || "https://api.dashtrack.com/contacts/_/contact_form_uploads";
824
+ const uploadFiles = useCallback(
825
+ async (files) => {
826
+ if (files.length === 0) return;
827
+ setIsUploading(true);
828
+ try {
829
+ const tokens = [];
830
+ for (const file of files) {
831
+ const formData = new FormData();
832
+ formData.append("contact_form_upload[file_upload]", file);
833
+ formData.append("contact_form_upload[title]", file.name);
834
+ formData.append("contact_form_upload[file_name]", file.name);
835
+ formData.append("contact_form_upload[file_size]", String(file.size));
836
+ const response = await fetch(endpoint, {
837
+ method: "POST",
838
+ body: formData
839
+ });
840
+ if (!response.ok) {
841
+ throw new Error(`Upload failed: ${response.statusText}`);
842
+ }
843
+ const data = await response.json();
844
+ if (data.contact_form_upload?.token) {
845
+ tokens.push(`upload_${data.contact_form_upload.token}`);
846
+ }
847
+ setUploadProgress((prev) => ({
848
+ ...prev,
849
+ [file.name]: 100
850
+ }));
851
+ }
852
+ setUploadTokens(tokens);
853
+ } catch (error) {
854
+ console.error("File upload error:", error);
855
+ options?.onError?.(error);
856
+ } finally {
857
+ setIsUploading(false);
858
+ }
859
+ },
860
+ [endpoint, options]
861
+ );
862
+ const removeFile = useCallback((file, index) => {
863
+ setUploadTokens((prev) => prev.filter((_, i) => i !== index));
864
+ setUploadProgress((prev) => {
865
+ const newProgress = { ...prev };
866
+ delete newProgress[file.name];
867
+ return newProgress;
868
+ });
869
+ }, []);
870
+ const resetUpload = useCallback(() => {
871
+ setUploadTokens([]);
872
+ setUploadProgress({});
873
+ }, []);
874
+ return {
875
+ uploadTokens,
876
+ uploadProgress,
877
+ isUploading,
878
+ uploadFiles,
879
+ removeFile,
880
+ resetUpload
881
+ };
882
+ }
883
+ function useContactForm(options) {
884
+ const {
885
+ formFields,
886
+ formConfig,
887
+ onSubmit,
888
+ onSuccess,
889
+ onError,
890
+ resetOnSuccess = true,
891
+ uploadTokens = []
892
+ } = options;
893
+ const [isSubmitted, setIsSubmitted] = useState(false);
894
+ const [submissionError, setSubmissionError] = useState(null);
895
+ const form = useForm({
896
+ initialValues: useMemo(
897
+ () => generateInitialValues(formFields),
898
+ [formFields]
899
+ ),
900
+ validationSchema: useMemo(
901
+ () => generateValidationSchema(formFields),
902
+ [formFields]
903
+ ),
904
+ onSubmit: async (values, helpers) => {
905
+ setSubmissionError(null);
906
+ const shouldAutoSubmit = Boolean(formConfig?.endpoint);
907
+ if (!shouldAutoSubmit && !onSubmit) {
908
+ return;
909
+ }
910
+ try {
911
+ let result;
912
+ const submissionValues = {
913
+ ...values,
914
+ ...uploadTokens.length > 0 && {
915
+ contact_form_upload_tokens: uploadTokens
916
+ }
917
+ };
918
+ if (shouldAutoSubmit) {
919
+ result = await submitPageSpeedForm(submissionValues, formConfig);
920
+ }
921
+ if (onSubmit) {
922
+ await onSubmit(submissionValues);
923
+ }
924
+ if (shouldAutoSubmit || onSubmit) {
925
+ setIsSubmitted(true);
926
+ if (resetOnSuccess) {
927
+ helpers.resetForm();
928
+ }
929
+ onSuccess?.(result);
930
+ setTimeout(() => setIsSubmitted(false), 5e3);
931
+ }
932
+ } catch (error) {
933
+ if (error instanceof PageSpeedFormSubmissionError && error.formErrors) {
934
+ helpers.setErrors(error.formErrors);
935
+ }
936
+ const errorMessage = error instanceof Error ? error.message : "Form submission failed";
937
+ setSubmissionError(errorMessage);
938
+ onError?.(error);
939
+ }
940
+ }
941
+ });
942
+ const formMethod = formConfig?.method?.toLowerCase() === "get" ? "get" : "post";
943
+ return {
944
+ form,
945
+ isSubmitted,
946
+ submissionError,
947
+ formMethod
948
+ };
949
+ }
950
+
951
+ // lib/forms.ts
569
952
  var PageSpeedFormSubmissionError = class extends Error {
570
953
  constructor(message, options = {}) {
571
954
  super(message);
@@ -1056,12 +1439,47 @@ var Section = React__default.forwardRef(
1056
1439
  }
1057
1440
  );
1058
1441
  Section.displayName = "Section";
1442
+ var DEFAULT_FORM_FIELDS = [
1443
+ {
1444
+ name: "firstName",
1445
+ type: "text",
1446
+ label: "First Name",
1447
+ placeholder: "First name",
1448
+ required: true,
1449
+ columnSpan: 6
1450
+ },
1451
+ {
1452
+ name: "lastName",
1453
+ type: "text",
1454
+ label: "Last Name",
1455
+ placeholder: "Last name",
1456
+ required: true,
1457
+ columnSpan: 6
1458
+ },
1459
+ {
1460
+ name: "email",
1461
+ type: "email",
1462
+ label: "Email Address",
1463
+ placeholder: "your@email.com",
1464
+ required: true,
1465
+ columnSpan: 12
1466
+ },
1467
+ {
1468
+ name: "message",
1469
+ type: "textarea",
1470
+ label: "Message",
1471
+ placeholder: "Your message...",
1472
+ required: true,
1473
+ rows: 4,
1474
+ columnSpan: 12
1475
+ }
1476
+ ];
1059
1477
  function ContactDark({
1060
1478
  heading,
1061
1479
  description,
1062
1480
  contactHeading = "Contact Information",
1063
1481
  contactDescription = "Fill up the form and our team will get back to you within 24 hours.",
1064
- buttonText,
1482
+ buttonText = "Submit",
1065
1483
  buttonIcon,
1066
1484
  actions,
1067
1485
  actionsSlot,
@@ -1069,6 +1487,9 @@ function ContactDark({
1069
1487
  contactOptionsSlot,
1070
1488
  socialLinks,
1071
1489
  socialLinksSlot,
1490
+ formFields = DEFAULT_FORM_FIELDS,
1491
+ successMessage = "Thank you! Your message has been sent successfully.",
1492
+ errorMessage = "There was an error sending your message. Please try again.",
1072
1493
  className,
1073
1494
  headerClassName,
1074
1495
  headingClassName,
@@ -1080,6 +1501,8 @@ function ContactDark({
1080
1501
  infoPanelClassName,
1081
1502
  contactOptionsClassName,
1082
1503
  socialLinksClassName,
1504
+ successMessageClassName,
1505
+ errorMessageClassName,
1083
1506
  background,
1084
1507
  spacing = "py-8 md:py-32",
1085
1508
  containerClassName = "px-6 sm:px-6 md:px-8 lg:px-8",
@@ -1090,53 +1513,26 @@ function ContactDark({
1090
1513
  onSuccess,
1091
1514
  onError
1092
1515
  }) {
1093
- const form = useForm({
1094
- initialValues: {
1095
- firstName: "",
1096
- lastName: "",
1097
- email: "",
1098
- message: ""
1099
- },
1100
- validationSchema: {
1101
- firstName: (value) => !value ? "First name is required" : void 0,
1102
- lastName: (value) => !value ? "Last name is required" : void 0,
1103
- email: (value) => {
1104
- if (!value) return "Email is required";
1105
- if (!/^[^\s@]+@[^\s@]+\.[^\s@]+$/.test(value))
1106
- return "Please enter a valid email address";
1107
- return void 0;
1108
- },
1109
- message: (value) => !value ? "Message is required" : void 0
1516
+ const {
1517
+ uploadTokens,
1518
+ uploadProgress,
1519
+ isUploading,
1520
+ uploadFiles,
1521
+ removeFile,
1522
+ resetUpload
1523
+ } = useFileUpload({ onError });
1524
+ const { form, isSubmitted, submissionError, formMethod } = useContactForm({
1525
+ formFields,
1526
+ formConfig,
1527
+ onSubmit,
1528
+ onSuccess: (data) => {
1529
+ resetUpload();
1530
+ onSuccess?.(data);
1110
1531
  },
1111
- onSubmit: async (values, helpers) => {
1112
- const shouldAutoSubmit = Boolean(formConfig?.endpoint);
1113
- if (!shouldAutoSubmit && !onSubmit) {
1114
- return;
1115
- }
1116
- try {
1117
- let result;
1118
- if (shouldAutoSubmit) {
1119
- result = await submitPageSpeedForm(values, formConfig);
1120
- }
1121
- if (onSubmit) {
1122
- await onSubmit(values);
1123
- }
1124
- if (shouldAutoSubmit || onSubmit) {
1125
- if (formConfig?.resetOnSuccess !== false) {
1126
- helpers.resetForm();
1127
- }
1128
- onSuccess?.(result);
1129
- }
1130
- } catch (error) {
1131
- if (error instanceof PageSpeedFormSubmissionError && error.formErrors) {
1132
- helpers.setErrors(error.formErrors);
1133
- }
1134
- onError?.(error);
1135
- throw error;
1136
- }
1137
- }
1532
+ onError,
1533
+ resetOnSuccess: formConfig?.resetOnSuccess !== false,
1534
+ uploadTokens
1138
1535
  });
1139
- const formMethod = formConfig?.method?.toLowerCase() === "get" ? "get" : "post";
1140
1536
  const actionsContent = React.useMemo(() => {
1141
1537
  if (actionsSlot) return actionsSlot;
1142
1538
  if (actions && actions.length > 0) {
@@ -1247,87 +1643,70 @@ function ContactDark({
1247
1643
  cardClassName
1248
1644
  ),
1249
1645
  children: [
1250
- /* @__PURE__ */ jsx("div", { className: cn("p-6 lg:p-12", formPanelClassName), children: /* @__PURE__ */ jsxs(
1251
- Form,
1252
- {
1253
- form,
1254
- action: formConfig?.endpoint,
1255
- method: formMethod,
1256
- className: cn("space-y-6", formClassName),
1257
- children: [
1258
- /* @__PURE__ */ jsxs("div", { className: "grid gap-4 sm:grid-cols-2", children: [
1259
- /* @__PURE__ */ jsx(Field, { name: "firstName", children: ({ field, meta }) => /* @__PURE__ */ jsxs("div", { className: "space-y-2", children: [
1260
- /* @__PURE__ */ jsx(Label, { htmlFor: "first-name", children: "First Name" }),
1261
- /* @__PURE__ */ jsx(
1262
- TextInput,
1263
- {
1264
- ...field,
1265
- id: "first-name",
1266
- placeholder: "First name",
1267
- error: meta.touched && !!meta.error,
1268
- "aria-label": "First Name"
1269
- }
1270
- )
1271
- ] }) }),
1272
- /* @__PURE__ */ jsx(Field, { name: "lastName", children: ({ field, meta }) => /* @__PURE__ */ jsxs("div", { className: "space-y-2", children: [
1273
- /* @__PURE__ */ jsx(Label, { htmlFor: "last-name", children: "Last Name" }),
1274
- /* @__PURE__ */ jsx(
1275
- TextInput,
1276
- {
1277
- ...field,
1278
- id: "last-name",
1279
- placeholder: "Last name",
1280
- error: meta.touched && !!meta.error,
1281
- "aria-label": "Last Name"
1282
- }
1283
- )
1284
- ] }) })
1285
- ] }),
1286
- /* @__PURE__ */ jsx(Field, { name: "email", children: ({ field, meta }) => /* @__PURE__ */ jsxs("div", { className: "space-y-2", children: [
1287
- /* @__PURE__ */ jsx(Label, { htmlFor: "email", children: "Email Address" }),
1288
- /* @__PURE__ */ jsx(
1289
- TextInput,
1646
+ /* @__PURE__ */ jsxs("div", { className: cn("p-6 lg:p-12", formPanelClassName), children: [
1647
+ isSubmitted && /* @__PURE__ */ jsx(
1648
+ "div",
1649
+ {
1650
+ className: cn(
1651
+ "mb-6 p-4 bg-primary/10 border border-primary rounded-md",
1652
+ successMessageClassName
1653
+ ),
1654
+ children: typeof successMessage === "string" ? /* @__PURE__ */ jsx("p", { className: "text-sm text-primary-foreground/90 text-center", children: successMessage }) : successMessage
1655
+ }
1656
+ ),
1657
+ submissionError && /* @__PURE__ */ jsx(
1658
+ "div",
1659
+ {
1660
+ className: cn(
1661
+ "mb-6 p-4 bg-destructive/10 border border-destructive rounded-md",
1662
+ errorMessageClassName
1663
+ ),
1664
+ children: /* @__PURE__ */ jsx("p", { className: "text-sm text-destructive text-center", children: submissionError })
1665
+ }
1666
+ ),
1667
+ /* @__PURE__ */ jsxs(
1668
+ Form,
1669
+ {
1670
+ form,
1671
+ action: formConfig?.endpoint,
1672
+ method: formMethod,
1673
+ className: cn("space-y-6", formClassName),
1674
+ children: [
1675
+ /* @__PURE__ */ jsx("div", { className: "grid grid-cols-12 gap-4", children: formFields.map((field) => /* @__PURE__ */ jsx(
1676
+ "div",
1290
1677
  {
1291
- ...field,
1292
- id: "email",
1293
- type: "email",
1294
- placeholder: "Your email",
1295
- error: meta.touched && !!meta.error,
1296
- "aria-label": "Email Address"
1297
- }
1298
- )
1299
- ] }) }),
1300
- /* @__PURE__ */ jsx(Field, { name: "message", children: ({ field, meta }) => /* @__PURE__ */ jsxs("div", { className: "space-y-2", children: [
1301
- /* @__PURE__ */ jsx(Label, { htmlFor: "message", children: "Message" }),
1302
- /* @__PURE__ */ jsx(
1303
- TextArea,
1678
+ className: getColumnSpanClass(field.columnSpan),
1679
+ children: /* @__PURE__ */ jsx(
1680
+ DynamicFormField,
1681
+ {
1682
+ field,
1683
+ uploadProgress,
1684
+ onFileUpload: uploadFiles,
1685
+ onFileRemove: removeFile,
1686
+ isUploading
1687
+ }
1688
+ )
1689
+ },
1690
+ field.name
1691
+ )) }),
1692
+ actionsSlot || actions && actions.length > 0 ? actionsContent : /* @__PURE__ */ jsxs(
1693
+ Pressable,
1304
1694
  {
1305
- ...field,
1306
- id: "message",
1307
- placeholder: "Your message...",
1308
- rows: 4,
1309
- error: meta.touched && !!meta.error,
1310
- "aria-label": "Message"
1695
+ componentType: "button",
1696
+ type: "submit",
1697
+ className: cn("w-full", submitClassName),
1698
+ asButton: true,
1699
+ disabled: form.isSubmitting,
1700
+ children: [
1701
+ buttonIcon,
1702
+ buttonText
1703
+ ]
1311
1704
  }
1312
1705
  )
1313
- ] }) }),
1314
- actionsSlot || actions && actions.length > 0 ? actionsContent : /* @__PURE__ */ jsxs(
1315
- Pressable,
1316
- {
1317
- componentType: "button",
1318
- type: "submit",
1319
- className: cn("w-full", submitClassName),
1320
- asButton: true,
1321
- disabled: form.isSubmitting,
1322
- children: [
1323
- buttonIcon,
1324
- buttonText
1325
- ]
1326
- }
1327
- )
1328
- ]
1329
- }
1330
- ) }),
1706
+ ]
1707
+ }
1708
+ )
1709
+ ] }),
1331
1710
  /* @__PURE__ */ jsxs(
1332
1711
  "div",
1333
1712
  {