@opensite/ui 1.9.8 → 2.0.0

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.
Files changed (131) hide show
  1. package/dist/contact-demo.cjs +106 -406
  2. package/dist/contact-demo.d.cts +36 -111
  3. package/dist/contact-demo.d.ts +36 -111
  4. package/dist/contact-demo.js +108 -405
  5. package/dist/contact-emergency.cjs +129 -158
  6. package/dist/contact-emergency.d.cts +23 -12
  7. package/dist/contact-emergency.d.ts +23 -12
  8. package/dist/contact-emergency.js +131 -159
  9. package/dist/contact-event.cjs +106 -147
  10. package/dist/contact-event.d.cts +36 -66
  11. package/dist/contact-event.d.ts +36 -66
  12. package/dist/contact-event.js +108 -148
  13. package/dist/contact-feedback.cjs +109 -102
  14. package/dist/contact-feedback.d.cts +36 -63
  15. package/dist/contact-feedback.d.ts +36 -63
  16. package/dist/contact-feedback.js +111 -103
  17. package/dist/contact-fitness.cjs +107 -148
  18. package/dist/contact-fitness.d.cts +36 -66
  19. package/dist/contact-fitness.d.ts +36 -66
  20. package/dist/contact-fitness.js +109 -149
  21. package/dist/contact-guest.cjs +107 -148
  22. package/dist/contact-guest.d.cts +35 -65
  23. package/dist/contact-guest.d.ts +35 -65
  24. package/dist/contact-guest.js +109 -149
  25. package/dist/contact-image.cjs +108 -149
  26. package/dist/contact-image.d.cts +35 -65
  27. package/dist/contact-image.d.ts +35 -65
  28. package/dist/contact-image.js +110 -150
  29. package/dist/contact-insurance.cjs +107 -148
  30. package/dist/contact-insurance.d.cts +36 -66
  31. package/dist/contact-insurance.d.ts +36 -66
  32. package/dist/contact-insurance.js +109 -149
  33. package/dist/contact-interview.cjs +106 -147
  34. package/dist/contact-interview.d.cts +20 -12
  35. package/dist/contact-interview.d.ts +20 -12
  36. package/dist/contact-interview.js +108 -148
  37. package/dist/contact-locations.cjs +106 -147
  38. package/dist/contact-locations.d.cts +21 -13
  39. package/dist/contact-locations.d.ts +21 -13
  40. package/dist/contact-locations.js +108 -148
  41. package/dist/contact-maintenance.cjs +103 -143
  42. package/dist/contact-maintenance.d.cts +21 -13
  43. package/dist/contact-maintenance.d.ts +21 -13
  44. package/dist/contact-maintenance.js +105 -144
  45. package/dist/contact-map.cjs +103 -143
  46. package/dist/contact-map.d.cts +20 -12
  47. package/dist/contact-map.d.ts +20 -12
  48. package/dist/contact-map.js +105 -144
  49. package/dist/contact-minimal.cjs +126 -242
  50. package/dist/contact-minimal.d.cts +36 -70
  51. package/dist/contact-minimal.d.ts +36 -70
  52. package/dist/contact-minimal.js +128 -243
  53. package/dist/contact-moving.cjs +103 -143
  54. package/dist/contact-moving.d.cts +20 -12
  55. package/dist/contact-moving.d.ts +20 -12
  56. package/dist/contact-moving.js +105 -144
  57. package/dist/contact-multistep.cjs +104 -144
  58. package/dist/contact-multistep.d.cts +21 -13
  59. package/dist/contact-multistep.d.ts +21 -13
  60. package/dist/contact-multistep.js +106 -145
  61. package/dist/contact-partnership.cjs +103 -143
  62. package/dist/contact-partnership.d.cts +19 -11
  63. package/dist/contact-partnership.d.ts +19 -11
  64. package/dist/contact-partnership.js +105 -144
  65. package/dist/contact-press.cjs +144 -150
  66. package/dist/contact-press.d.cts +21 -13
  67. package/dist/contact-press.d.ts +21 -13
  68. package/dist/contact-press.js +146 -151
  69. package/dist/contact-quote.cjs +144 -150
  70. package/dist/contact-quote.d.cts +19 -11
  71. package/dist/contact-quote.d.ts +19 -11
  72. package/dist/contact-quote.js +146 -151
  73. package/dist/contact-referral.cjs +144 -150
  74. package/dist/contact-referral.d.cts +20 -12
  75. package/dist/contact-referral.d.ts +20 -12
  76. package/dist/contact-referral.js +146 -151
  77. package/dist/contact-report.cjs +144 -150
  78. package/dist/contact-report.d.cts +21 -13
  79. package/dist/contact-report.d.ts +21 -13
  80. package/dist/contact-report.js +146 -151
  81. package/dist/contact-reservation.cjs +144 -150
  82. package/dist/contact-reservation.d.cts +20 -12
  83. package/dist/contact-reservation.d.ts +20 -12
  84. package/dist/contact-reservation.js +146 -151
  85. package/dist/contact-retreat.cjs +144 -150
  86. package/dist/contact-retreat.d.cts +21 -13
  87. package/dist/contact-retreat.d.ts +21 -13
  88. package/dist/contact-retreat.js +146 -151
  89. package/dist/contact-rsvp.cjs +144 -150
  90. package/dist/contact-rsvp.d.cts +20 -12
  91. package/dist/contact-rsvp.d.ts +20 -12
  92. package/dist/contact-rsvp.js +146 -151
  93. package/dist/contact-sales.cjs +131 -146
  94. package/dist/contact-sales.d.cts +34 -65
  95. package/dist/contact-sales.d.ts +34 -65
  96. package/dist/contact-sales.js +133 -147
  97. package/dist/contact-schedule.cjs +144 -150
  98. package/dist/contact-schedule.d.cts +20 -12
  99. package/dist/contact-schedule.d.ts +20 -12
  100. package/dist/contact-schedule.js +146 -151
  101. package/dist/contact-sponsorship.cjs +144 -150
  102. package/dist/contact-sponsorship.d.cts +21 -13
  103. package/dist/contact-sponsorship.d.ts +21 -13
  104. package/dist/contact-sponsorship.js +146 -151
  105. package/dist/contact-support.cjs +147 -120
  106. package/dist/contact-support.d.cts +21 -12
  107. package/dist/contact-support.d.ts +21 -12
  108. package/dist/contact-support.js +149 -121
  109. package/dist/contact-tenant.cjs +144 -150
  110. package/dist/contact-tenant.d.cts +20 -12
  111. package/dist/contact-tenant.d.ts +20 -12
  112. package/dist/contact-tenant.js +146 -151
  113. package/dist/contact-vendor.cjs +144 -150
  114. package/dist/contact-vendor.d.cts +20 -12
  115. package/dist/contact-vendor.d.ts +20 -12
  116. package/dist/contact-vendor.js +146 -151
  117. package/dist/contact-volunteer.cjs +144 -150
  118. package/dist/contact-volunteer.d.cts +20 -12
  119. package/dist/contact-volunteer.d.ts +20 -12
  120. package/dist/contact-volunteer.js +146 -151
  121. package/dist/contact-warranty.cjs +144 -150
  122. package/dist/contact-warranty.d.cts +20 -12
  123. package/dist/contact-warranty.d.ts +20 -12
  124. package/dist/contact-warranty.js +146 -151
  125. package/dist/contact-wedding.cjs +144 -150
  126. package/dist/contact-wedding.d.cts +20 -12
  127. package/dist/contact-wedding.d.ts +20 -12
  128. package/dist/contact-wedding.js +146 -151
  129. package/dist/registry.cjs +5791 -5883
  130. package/dist/registry.js +4527 -4619
  131. package/package.json +2 -2
@@ -1,5 +1,5 @@
1
1
  import * as React from 'react';
2
- import { PageSpeedFormConfig } from '@page-speed/forms/integration';
2
+ import { FormFieldConfig, PageSpeedFormConfig } from '@page-speed/forms/integration';
3
3
  import { f as SectionBackground, g as SectionSpacing, t as PatternName } from './community-initiatives-Bi_ClKrO.js';
4
4
  import { A as ActionConfig } from './blocks-k17uluAz.js';
5
5
  import 'react/jsx-runtime';
@@ -7,76 +7,51 @@ import 'class-variance-authority';
7
7
  import './button-variants-lRElsmTc.js';
8
8
  import 'class-variance-authority/types';
9
9
 
10
- interface ContactMinimalFormValues {
11
- name: string;
12
- email: string;
13
- message: string;
14
- }
15
10
  interface ContactMinimalProps {
16
- /**
17
- * Main heading content
18
- */
11
+ /** Main heading text */
19
12
  heading?: React.ReactNode;
20
- /**
21
- * Description text below heading
22
- */
13
+ /** Description text below heading */
23
14
  description?: React.ReactNode;
24
- /**
25
- * Submit button text
26
- */
15
+ /** Submit button text */
27
16
  buttonText?: string;
28
- /**
29
- * Submit button icon (displayed before text)
30
- */
17
+ /** Icon to display in submit button */
31
18
  buttonIcon?: React.ReactNode;
32
- /**
33
- * Array of action configurations for additional buttons
34
- */
19
+ /** Array of action configurations for custom buttons */
35
20
  actions?: ActionConfig[];
36
- /**
37
- * Custom slot for rendering actions (overrides actions array and default submit)
38
- */
21
+ /** Custom slot for rendering actions (overrides actions array) */
39
22
  actionsSlot?: React.ReactNode;
40
23
  /**
41
- * Footer content (e.g., privacy policy text)
42
- */
43
- footer?: React.ReactNode;
44
- /**
45
- * Custom slot for footer content (overrides footer prop)
24
+ * Array of form field configurations
25
+ * If not provided, defaults to: first_name, last_name, email, phone, message
46
26
  */
47
- footerSlot?: React.ReactNode;
27
+ formFields?: FormFieldConfig[];
48
28
  /**
49
- * Additional CSS classes for the section
29
+ * Success message to display after form submission
30
+ * @default "Thank you! Your message has been sent successfully."
50
31
  */
32
+ successMessage?: React.ReactNode;
33
+ /** Additional CSS classes for the section */
51
34
  className?: string;
52
- /**
53
- * Additional CSS classes for the container
54
- */
35
+ /** Additional CSS classes for the container */
55
36
  containerClassName?: string;
56
- /**
57
- * Additional CSS classes for the header wrapper
58
- */
37
+ /** Additional CSS classes for the header */
59
38
  headerClassName?: string;
60
- /**
61
- * Additional CSS classes for the heading
62
- */
39
+ /** Additional CSS classes for the heading */
63
40
  headingClassName?: string;
64
- /**
65
- * Additional CSS classes for the description
66
- */
41
+ /** Additional CSS classes for the description */
67
42
  descriptionClassName?: string;
68
- /**
69
- * Additional CSS classes for the form
70
- */
43
+ /** Additional CSS classes for the card */
44
+ cardClassName?: string;
45
+ /** Additional CSS classes for the card content */
46
+ cardContentClassName?: string;
47
+ /** Additional CSS classes for the form */
71
48
  formClassName?: string;
72
- /**
73
- * Additional CSS classes for the submit button
74
- */
49
+ /** Additional CSS classes for the submit button */
75
50
  submitClassName?: string;
76
- /**
77
- * Additional CSS classes for the footer
78
- */
79
- footerClassName?: string;
51
+ /** Additional CSS classes for the success message */
52
+ successMessageClassName?: string;
53
+ /** Additional CSS classes for the error message */
54
+ errorMessageClassName?: string;
80
55
  /**
81
56
  * Background style for the section
82
57
  */
@@ -93,35 +68,26 @@ interface ContactMinimalProps {
93
68
  * Pattern overlay opacity (0-1)
94
69
  */
95
70
  patternOpacity?: number;
96
- /**
97
- * Form submission configuration
98
- */
71
+ /** Form configuration for PageSpeed forms */
99
72
  formConfig?: PageSpeedFormConfig;
100
- /**
101
- * Custom submission handler
102
- */
103
- onSubmit?: (values: ContactMinimalFormValues) => void | Promise<void>;
104
- /**
105
- * Success callback after submission
106
- */
73
+ /** Custom submit handler */
74
+ onSubmit?: (values: Record<string, any>) => void | Promise<void>;
75
+ /** Success callback */
107
76
  onSuccess?: (data: unknown) => void;
108
- /**
109
- * Error callback if submission fails
110
- */
77
+ /** Error callback */
111
78
  onError?: (error: Error) => void;
112
79
  }
113
80
  /**
114
- * ContactMinimal - Simple, clean contact form with essential fields.
115
- * Perfect for minimal designs and quick contact forms.
81
+ * ContactMinimal - Contact form with flexible field configuration
116
82
  *
117
83
  * @example
118
84
  * ```tsx
119
85
  * <ContactMinimal
120
- * heading="Let's Talk"
86
+ * heading="Contact Us"
121
87
  * formConfig={{ endpoint: "/api/contact", format: "json" }}
122
88
  * />
123
89
  * ```
124
90
  */
125
- declare function ContactMinimal({ heading, description, buttonText, buttonIcon, actions, actionsSlot, footer, footerSlot, className, containerClassName, headerClassName, headingClassName, descriptionClassName, formClassName, submitClassName, footerClassName, background, spacing, pattern, patternOpacity, formConfig, onSubmit, onSuccess, onError, }: ContactMinimalProps): React.JSX.Element;
91
+ declare function ContactMinimal({ heading, description, buttonText, buttonIcon, actions, actionsSlot, formFields, successMessage, className, containerClassName, headerClassName, headingClassName, descriptionClassName, cardClassName, cardContentClassName, formClassName, submitClassName, successMessageClassName, errorMessageClassName, background, spacing, pattern, patternOpacity, formConfig, onSubmit, onSuccess, onError, }: ContactMinimalProps): React.JSX.Element;
126
92
 
127
93
  export { ContactMinimal, type ContactMinimalProps };
@@ -1,18 +1,14 @@
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 as TextInput$1, TextArea as TextArea$1 } from '@page-speed/forms/inputs';
3
+ import React__default, { useMemo } from 'react';
4
+ import { Form } from '@page-speed/forms';
5
+ import { useFileUpload, useContactForm, getColumnSpanClass, DynamicFormField } from '@page-speed/forms/integration';
6
6
  import { clsx } from 'clsx';
7
7
  import { twMerge } from 'tailwind-merge';
8
8
  import { cva } from 'class-variance-authority';
9
9
  import { jsx, jsxs, Fragment } from 'react/jsx-runtime';
10
- import * as LabelPrimitive from '@radix-ui/react-label';
11
- import { submitPageSpeedForm, PageSpeedFormSubmissionError } from '@page-speed/forms/integration';
12
10
 
13
11
  // components/blocks/contact/contact-minimal.tsx
14
- var TextInput = TextInput$1;
15
- var TextArea = TextArea$1;
16
12
  function cn(...inputs) {
17
13
  return twMerge(clsx(inputs));
18
14
  }
@@ -434,123 +430,25 @@ var Pressable = React.forwardRef(
434
430
  }
435
431
  );
436
432
  Pressable.displayName = "Pressable";
437
- var svgCache = /* @__PURE__ */ new Map();
438
- function DynamicIcon({
439
- name,
440
- size = 28,
441
- color,
442
- className,
443
- alt
444
- }) {
445
- const [svgContent, setSvgContent] = React.useState(null);
446
- const [isLoading, setIsLoading] = React.useState(true);
447
- const [error, setError] = React.useState(null);
448
- const { url, iconName } = React.useMemo(() => {
449
- const separator = name.includes("/") ? "/" : ":";
450
- const [prefix, iconName2] = name.split(separator);
451
- const baseUrl = `https://icons.opensite.ai/api/icon/${prefix}/${iconName2}?format=svg&width=${size}&height=${size}&key=au382bi7fsh96w9h9xlrnat2jglx`;
452
- return {
453
- url: baseUrl,
454
- iconName: iconName2
455
- };
456
- }, [name, size]);
457
- React.useEffect(() => {
458
- let isMounted = true;
459
- const fetchSvg = async () => {
460
- const cached = svgCache.get(url);
461
- if (cached) {
462
- if (isMounted) {
463
- setSvgContent(cached);
464
- setIsLoading(false);
465
- }
466
- return;
467
- }
468
- try {
469
- setIsLoading(true);
470
- setError(null);
471
- const response = await fetch(url);
472
- if (!response.ok) {
473
- throw new Error(`Failed to fetch icon: ${response.status}`);
474
- }
475
- let svg = await response.text();
476
- svg = processSvgForCurrentColor(svg);
477
- svgCache.set(url, svg);
478
- if (isMounted) {
479
- setSvgContent(svg);
480
- setIsLoading(false);
481
- }
482
- } catch (err) {
483
- if (isMounted) {
484
- setError(err instanceof Error ? err.message : "Failed to load icon");
485
- setIsLoading(false);
486
- }
487
- }
488
- };
489
- fetchSvg();
490
- return () => {
491
- isMounted = false;
492
- };
493
- }, [url]);
494
- if (isLoading) {
495
- return /* @__PURE__ */ jsx(
496
- "span",
497
- {
498
- className: cn("inline-block", className),
499
- style: { width: size, height: size },
500
- "aria-hidden": "true"
501
- }
502
- );
503
- }
504
- if (error || !svgContent) {
505
- return /* @__PURE__ */ jsx(
506
- "span",
507
- {
508
- className: cn("inline-block", className),
509
- style: { width: size, height: size },
510
- role: "img",
511
- "aria-label": alt || iconName
512
- }
513
- );
514
- }
433
+ function Card({ className, ...props }) {
515
434
  return /* @__PURE__ */ jsx(
516
- "span",
435
+ "div",
517
436
  {
518
- className: cn("inline-flex items-center justify-center", className),
519
- style: {
520
- width: size,
521
- height: size,
522
- color: color || "inherit"
523
- },
524
- role: "img",
525
- "aria-label": alt || iconName,
526
- dangerouslySetInnerHTML: { __html: svgContent }
437
+ "data-slot": "card",
438
+ className: cn(
439
+ "bg-card text-card-foreground flex flex-col gap-6 rounded-xl border py-6 shadow-sm",
440
+ className
441
+ ),
442
+ ...props
527
443
  }
528
444
  );
529
445
  }
530
- function processSvgForCurrentColor(svg) {
531
- let processed = svg;
532
- processed = processed.replace(
533
- /stroke=["'](#000000|#000|black)["']/gi,
534
- 'stroke="currentColor"'
535
- );
536
- processed = processed.replace(
537
- /fill=["'](#000000|#000|black)["']/gi,
538
- 'fill="currentColor"'
539
- );
540
- return processed;
541
- }
542
- function Label({
543
- className,
544
- ...props
545
- }) {
446
+ function CardContent({ className, ...props }) {
546
447
  return /* @__PURE__ */ jsx(
547
- LabelPrimitive.Root,
448
+ "div",
548
449
  {
549
- "data-slot": "label",
550
- className: cn(
551
- "flex items-center gap-2 text-sm leading-none font-medium select-none group-data-[disabled=true]:pointer-events-none group-data-[disabled=true]:opacity-50 peer-disabled:cursor-not-allowed peer-disabled:opacity-50",
552
- className
553
- ),
450
+ "data-slot": "card-content",
451
+ className: cn("px-6", className),
554
452
  ...props
555
453
  }
556
454
  );
@@ -930,23 +828,69 @@ var Section = React__default.forwardRef(
930
828
  }
931
829
  );
932
830
  Section.displayName = "Section";
831
+ var DEFAULT_FORM_FIELDS = [
832
+ {
833
+ name: "first_name",
834
+ type: "text",
835
+ label: "First Name",
836
+ placeholder: "John",
837
+ required: true,
838
+ columnSpan: 6
839
+ },
840
+ {
841
+ name: "last_name",
842
+ type: "text",
843
+ label: "Last Name",
844
+ placeholder: "Doe",
845
+ required: true,
846
+ columnSpan: 6
847
+ },
848
+ {
849
+ name: "email",
850
+ type: "email",
851
+ label: "Email",
852
+ placeholder: "john@example.com",
853
+ required: true,
854
+ columnSpan: 12
855
+ },
856
+ {
857
+ name: "phone",
858
+ type: "tel",
859
+ label: "Phone",
860
+ placeholder: "+1 (555) 000-0000",
861
+ required: true,
862
+ columnSpan: 12
863
+ },
864
+ {
865
+ name: "message",
866
+ type: "textarea",
867
+ label: "Message",
868
+ placeholder: "Your message...",
869
+ required: true,
870
+ rows: 4,
871
+ columnSpan: 12
872
+ }
873
+ ];
933
874
  function ContactMinimal({
934
875
  heading,
935
876
  description,
936
- buttonText,
937
- buttonIcon = /* @__PURE__ */ jsx(DynamicIcon, { name: "lucide/send", size: 16 }),
877
+ buttonText = "Submit",
878
+ buttonIcon,
938
879
  actions,
939
880
  actionsSlot,
940
- footer,
941
- footerSlot,
881
+ formFields = DEFAULT_FORM_FIELDS,
882
+ successMessage = "Thank you! Your message has been sent successfully.",
942
883
  className,
943
- containerClassName,
884
+ containerClassName = "px-6 sm:px-6 md:px-8 lg:px-8",
944
885
  headerClassName,
945
886
  headingClassName,
946
887
  descriptionClassName,
888
+ cardClassName,
889
+ cardContentClassName,
947
890
  formClassName,
948
891
  submitClassName,
949
- footerClassName,
892
+ successMessageClassName,
893
+ errorMessageClassName,
950
894
  background = "white",
951
895
  spacing = "xl",
952
896
  pattern,
@@ -956,52 +900,27 @@ function ContactMinimal({
956
900
  onSuccess,
957
901
  onError
958
902
  }) {
959
- const form = useForm({
960
- initialValues: {
961
- name: "",
962
- email: "",
963
- message: ""
903
+ const {
904
+ uploadTokens,
905
+ uploadProgress,
906
+ isUploading,
907
+ uploadFiles,
908
+ removeFile,
909
+ resetUpload
910
+ } = useFileUpload({ onError });
911
+ const { form, submissionError, formMethod, resetSubmissionState } = useContactForm({
912
+ formFields,
913
+ formConfig,
914
+ onSubmit,
915
+ onSuccess: (data) => {
916
+ resetUpload();
917
+ onSuccess?.(data);
964
918
  },
965
- validationSchema: {
966
- name: (value) => !value ? "Name is required" : void 0,
967
- email: (value) => {
968
- if (!value) return "Email is required";
969
- if (!/^[^\s@]+@[^\s@]+\.[^\s@]+$/.test(value))
970
- return "Please enter a valid email address";
971
- return void 0;
972
- },
973
- message: (value) => !value ? "Message is required" : void 0
974
- },
975
- onSubmit: async (values, helpers) => {
976
- const shouldAutoSubmit = Boolean(formConfig?.endpoint);
977
- if (!shouldAutoSubmit && !onSubmit) {
978
- return;
979
- }
980
- try {
981
- let result;
982
- if (shouldAutoSubmit) {
983
- result = await submitPageSpeedForm(values, formConfig);
984
- }
985
- if (onSubmit) {
986
- await onSubmit(values);
987
- }
988
- if (shouldAutoSubmit || onSubmit) {
989
- if (formConfig?.resetOnSuccess !== false) {
990
- helpers.resetForm();
991
- }
992
- onSuccess?.(result);
993
- }
994
- } catch (error) {
995
- if (error instanceof PageSpeedFormSubmissionError && error.formErrors) {
996
- helpers.setErrors(error.formErrors);
997
- }
998
- onError?.(error);
999
- throw error;
1000
- }
1001
- }
919
+ onError,
920
+ resetOnSuccess: formConfig?.resetOnSuccess !== false,
921
+ uploadTokens
1002
922
  });
1003
- const formMethod = formConfig?.method?.toLowerCase() === "get" ? "get" : "post";
1004
- const actionsContent = React.useMemo(() => {
923
+ const actionsContent = useMemo(() => {
1005
924
  if (actionsSlot) return actionsSlot;
1006
925
  if (actions && actions.length > 0) {
1007
926
  return actions.map((action, index) => {
@@ -1031,31 +950,6 @@ function ContactMinimal({
1031
950
  }
1032
951
  return null;
1033
952
  }, [actionsSlot, actions]);
1034
- const footerContent = React.useMemo(() => {
1035
- if (footerSlot) return footerSlot;
1036
- if (footer) {
1037
- return typeof footer === "string" ? /* @__PURE__ */ jsx(
1038
- "p",
1039
- {
1040
- className: cn(
1041
- "mt-6 text-center text-sm text-balance",
1042
- footerClassName
1043
- ),
1044
- children: footer
1045
- }
1046
- ) : /* @__PURE__ */ jsx(
1047
- "div",
1048
- {
1049
- className: cn(
1050
- "mt-6 text-center text-sm text-balance",
1051
- footerClassName
1052
- ),
1053
- children: footer
1054
- }
1055
- );
1056
- }
1057
- return null;
1058
- }, [footerSlot, footer, footerClassName]);
1059
953
  return /* @__PURE__ */ jsx(
1060
954
  Section,
1061
955
  {
@@ -1064,13 +958,14 @@ function ContactMinimal({
1064
958
  pattern,
1065
959
  patternOpacity,
1066
960
  className: cn("py-12", className),
1067
- children: /* @__PURE__ */ jsxs("div", { className: cn("mx-auto w-full max-w-md px-4", containerClassName), children: [
961
+ containerClassName,
962
+ children: /* @__PURE__ */ jsxs("div", { className: "mx-auto max-w-4xl", children: [
1068
963
  /* @__PURE__ */ jsxs("div", { className: cn("mb-10 text-center", headerClassName), children: [
1069
964
  heading && (typeof heading === "string" ? /* @__PURE__ */ jsx(
1070
965
  "h2",
1071
966
  {
1072
967
  className: cn(
1073
- "mb-3 text-3xl font-bold tracking-tight",
968
+ "mb-3 text-3xl font-bold tracking-tight text-balance",
1074
969
  headingClassName
1075
970
  ),
1076
971
  children: heading
@@ -1080,68 +975,59 @@ function ContactMinimal({
1080
975
  "p",
1081
976
  {
1082
977
  className: cn(
1083
- "leading-relaxed text-muted-foreground",
978
+ "leading-relaxed text-balance",
1084
979
  descriptionClassName
1085
980
  ),
1086
981
  children: description
1087
982
  }
1088
983
  ) : /* @__PURE__ */ jsx("div", { className: descriptionClassName, children: description }))
1089
984
  ] }),
1090
- /* @__PURE__ */ jsxs(
985
+ /* @__PURE__ */ jsx(Card, { className: cn("mx-auto max-w-xl", cardClassName), children: /* @__PURE__ */ jsx(CardContent, { className: cn("p-6 lg:p-8", cardContentClassName), children: /* @__PURE__ */ jsxs(
1091
986
  Form,
1092
987
  {
1093
988
  form,
1094
- action: formConfig?.endpoint,
1095
- method: formMethod,
1096
- className: cn("space-y-4", formClassName),
989
+ notificationConfig: {
990
+ submissionError,
991
+ successMessage
992
+ },
993
+ styleConfig: {
994
+ formClassName: cn("space-y-4", formClassName),
995
+ successMessageClassName,
996
+ errorMessageClassName
997
+ },
998
+ formConfig: {
999
+ endpoint: formConfig?.endpoint,
1000
+ method: formMethod,
1001
+ submissionConfig: formConfig?.submissionConfig
1002
+ },
1003
+ onNewSubmission: () => {
1004
+ resetUpload();
1005
+ resetSubmissionState();
1006
+ },
1097
1007
  children: [
1098
- /* @__PURE__ */ jsx(Field, { name: "name", children: ({ field, meta }) => /* @__PURE__ */ jsxs("div", { className: "space-y-2", children: [
1099
- /* @__PURE__ */ jsx(Label, { htmlFor: "name", children: "Name" }),
1100
- /* @__PURE__ */ jsx(
1101
- TextInput,
1102
- {
1103
- ...field,
1104
- id: "name",
1105
- placeholder: "Your full name",
1106
- error: meta.touched && !!meta.error,
1107
- "aria-label": "Name"
1108
- }
1109
- )
1110
- ] }) }),
1111
- /* @__PURE__ */ jsx(Field, { name: "email", children: ({ field, meta }) => /* @__PURE__ */ jsxs("div", { className: "space-y-2", children: [
1112
- /* @__PURE__ */ jsx(Label, { htmlFor: "email", children: "Email" }),
1113
- /* @__PURE__ */ jsx(
1114
- TextInput,
1115
- {
1116
- ...field,
1117
- id: "email",
1118
- type: "email",
1119
- placeholder: "your@email.com",
1120
- error: meta.touched && !!meta.error,
1121
- "aria-label": "Email"
1122
- }
1123
- )
1124
- ] }) }),
1125
- /* @__PURE__ */ jsx(Field, { name: "message", children: ({ field, meta }) => /* @__PURE__ */ jsxs("div", { className: "space-y-2", children: [
1126
- /* @__PURE__ */ jsx(Label, { htmlFor: "message", children: "Message" }),
1127
- /* @__PURE__ */ jsx(
1128
- TextArea,
1129
- {
1130
- ...field,
1131
- id: "message",
1132
- placeholder: "Tell us what's on your mind...",
1133
- rows: 4,
1134
- error: meta.touched && !!meta.error,
1135
- "aria-label": "Message"
1136
- }
1137
- )
1138
- ] }) }),
1008
+ /* @__PURE__ */ jsx("div", { className: "grid grid-cols-12 gap-6", children: formFields.map((field) => /* @__PURE__ */ jsx(
1009
+ "div",
1010
+ {
1011
+ className: getColumnSpanClass(field.columnSpan),
1012
+ children: /* @__PURE__ */ jsx(
1013
+ DynamicFormField,
1014
+ {
1015
+ field,
1016
+ uploadProgress,
1017
+ onFileUpload: uploadFiles,
1018
+ onFileRemove: removeFile,
1019
+ isUploading
1020
+ }
1021
+ )
1022
+ },
1023
+ field.name
1024
+ )) }),
1139
1025
  actionsSlot || actions && actions.length > 0 ? actionsContent : /* @__PURE__ */ jsxs(
1140
1026
  Pressable,
1141
1027
  {
1142
1028
  componentType: "button",
1143
1029
  type: "submit",
1144
- className: cn("w-full gap-2", submitClassName),
1030
+ className: cn("w-full", submitClassName),
1145
1031
  size: "lg",
1146
1032
  asButton: true,
1147
1033
  disabled: form.isSubmitting,
@@ -1153,8 +1039,7 @@ function ContactMinimal({
1153
1039
  )
1154
1040
  ]
1155
1041
  }
1156
- ),
1157
- footerContent
1042
+ ) }) })
1158
1043
  ] })
1159
1044
  }
1160
1045
  );