@opensite/ui 1.8.2 → 1.8.4

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 (171) hide show
  1. package/dist/about-story-gallery.cjs +3 -30
  2. package/dist/about-story-gallery.d.cts +1 -1
  3. package/dist/about-story-gallery.d.ts +1 -1
  4. package/dist/about-story-gallery.js +3 -30
  5. package/dist/components.d.cts +1 -1
  6. package/dist/components.d.ts +1 -1
  7. package/dist/contact-callback.cjs +526 -273
  8. package/dist/contact-callback.d.cts +39 -59
  9. package/dist/contact-callback.d.ts +39 -59
  10. package/dist/contact-callback.js +528 -274
  11. package/dist/contact-card.cjs +459 -183
  12. package/dist/contact-card.d.cts +26 -49
  13. package/dist/contact-card.d.ts +26 -49
  14. package/dist/contact-card.js +461 -183
  15. package/dist/contact-careers.cjs +614 -510
  16. package/dist/contact-careers.d.cts +32 -55
  17. package/dist/contact-careers.d.ts +32 -55
  18. package/dist/contact-careers.js +616 -510
  19. package/dist/contact-catering.cjs +507 -501
  20. package/dist/contact-catering.d.cts +27 -61
  21. package/dist/contact-catering.d.ts +27 -61
  22. package/dist/contact-catering.js +509 -500
  23. package/dist/contact-consultation.cjs +484 -253
  24. package/dist/contact-consultation.d.cts +29 -56
  25. package/dist/contact-consultation.d.ts +29 -56
  26. package/dist/contact-consultation.js +486 -253
  27. package/dist/contact-dark.cjs +296 -296
  28. package/dist/contact-dark.d.cts +1 -1
  29. package/dist/contact-dark.d.ts +1 -1
  30. package/dist/contact-dark.js +297 -296
  31. package/dist/contact-demo.d.cts +1 -1
  32. package/dist/contact-demo.d.ts +1 -1
  33. package/dist/contact-emergency.d.cts +1 -1
  34. package/dist/contact-emergency.d.ts +1 -1
  35. package/dist/contact-event.d.cts +1 -1
  36. package/dist/contact-event.d.ts +1 -1
  37. package/dist/contact-faq.cjs +247 -250
  38. package/dist/contact-faq.d.cts +1 -1
  39. package/dist/contact-faq.d.ts +1 -1
  40. package/dist/contact-faq.js +248 -250
  41. package/dist/contact-feedback.d.cts +1 -1
  42. package/dist/contact-feedback.d.ts +1 -1
  43. package/dist/contact-fitness.d.cts +1 -1
  44. package/dist/contact-fitness.d.ts +1 -1
  45. package/dist/contact-guest.d.cts +1 -1
  46. package/dist/contact-guest.d.ts +1 -1
  47. package/dist/contact-image.d.cts +1 -1
  48. package/dist/contact-image.d.ts +1 -1
  49. package/dist/contact-insurance.d.cts +1 -1
  50. package/dist/contact-insurance.d.ts +1 -1
  51. package/dist/contact-interview.d.cts +1 -1
  52. package/dist/contact-interview.d.ts +1 -1
  53. package/dist/contact-locations.d.cts +1 -1
  54. package/dist/contact-locations.d.ts +1 -1
  55. package/dist/contact-maintenance.d.cts +1 -1
  56. package/dist/contact-maintenance.d.ts +1 -1
  57. package/dist/contact-map.d.cts +1 -1
  58. package/dist/contact-map.d.ts +1 -1
  59. package/dist/contact-minimal.d.cts +1 -1
  60. package/dist/contact-minimal.d.ts +1 -1
  61. package/dist/contact-moving.d.cts +1 -1
  62. package/dist/contact-moving.d.ts +1 -1
  63. package/dist/contact-multistep.d.cts +1 -1
  64. package/dist/contact-multistep.d.ts +1 -1
  65. package/dist/contact-partnership.d.cts +1 -1
  66. package/dist/contact-partnership.d.ts +1 -1
  67. package/dist/contact-photography.cjs +247 -250
  68. package/dist/contact-photography.d.cts +1 -1
  69. package/dist/contact-photography.d.ts +1 -1
  70. package/dist/contact-photography.js +248 -250
  71. package/dist/contact-press.d.cts +1 -1
  72. package/dist/contact-press.d.ts +1 -1
  73. package/dist/contact-quote.d.cts +1 -1
  74. package/dist/contact-quote.d.ts +1 -1
  75. package/dist/contact-referral.d.cts +1 -1
  76. package/dist/contact-referral.d.ts +1 -1
  77. package/dist/contact-report.d.cts +1 -1
  78. package/dist/contact-report.d.ts +1 -1
  79. package/dist/contact-reservation.d.cts +1 -1
  80. package/dist/contact-reservation.d.ts +1 -1
  81. package/dist/contact-retreat.d.cts +1 -1
  82. package/dist/contact-retreat.d.ts +1 -1
  83. package/dist/contact-rsvp.d.cts +1 -1
  84. package/dist/contact-rsvp.d.ts +1 -1
  85. package/dist/contact-sales.d.cts +1 -1
  86. package/dist/contact-sales.d.ts +1 -1
  87. package/dist/contact-schedule.d.cts +1 -1
  88. package/dist/contact-schedule.d.ts +1 -1
  89. package/dist/contact-sponsorship.d.cts +1 -1
  90. package/dist/contact-sponsorship.d.ts +1 -1
  91. package/dist/contact-support.d.cts +1 -1
  92. package/dist/contact-support.d.ts +1 -1
  93. package/dist/contact-tenant.d.cts +1 -1
  94. package/dist/contact-tenant.d.ts +1 -1
  95. package/dist/contact-vendor.d.cts +1 -1
  96. package/dist/contact-vendor.d.ts +1 -1
  97. package/dist/contact-volunteer.d.cts +1 -1
  98. package/dist/contact-volunteer.d.ts +1 -1
  99. package/dist/contact-warranty.d.cts +1 -1
  100. package/dist/contact-warranty.d.ts +1 -1
  101. package/dist/contact-wedding.d.cts +1 -1
  102. package/dist/contact-wedding.d.ts +1 -1
  103. package/dist/cta-app-download-newsletter.d.cts +1 -1
  104. package/dist/cta-app-download-newsletter.d.ts +1 -1
  105. package/dist/cta-newsletter-features.d.cts +1 -1
  106. package/dist/cta-newsletter-features.d.ts +1 -1
  107. package/dist/footer-accordion-social.d.cts +1 -1
  108. package/dist/footer-accordion-social.d.ts +1 -1
  109. package/dist/footer-newsletter-contact.d.cts +1 -1
  110. package/dist/footer-newsletter-contact.d.ts +1 -1
  111. package/dist/footer-newsletter-minimal.d.cts +1 -1
  112. package/dist/footer-newsletter-minimal.d.ts +1 -1
  113. package/dist/footer-split-image-accordion.d.cts +1 -1
  114. package/dist/footer-split-image-accordion.d.ts +1 -1
  115. package/dist/{forms-nGgHUTBw.d.cts → forms-CStlFhnh.d.cts} +41 -0
  116. package/dist/{forms-nGgHUTBw.d.ts → forms-CStlFhnh.d.ts} +41 -0
  117. package/dist/hero-conversation-intelligence.cjs +1 -2
  118. package/dist/hero-conversation-intelligence.d.cts +1 -5
  119. package/dist/hero-conversation-intelligence.d.ts +1 -5
  120. package/dist/hero-conversation-intelligence.js +1 -2
  121. package/dist/hero-conversion-video-play.cjs +2 -2
  122. package/dist/hero-conversion-video-play.js +2 -2
  123. package/dist/hero-design-system-3d.cjs +162 -82
  124. package/dist/hero-design-system-3d.js +162 -82
  125. package/dist/hero-ecommerce-product-showcase.cjs +103 -81
  126. package/dist/hero-ecommerce-product-showcase.d.cts +5 -1
  127. package/dist/hero-ecommerce-product-showcase.d.ts +5 -1
  128. package/dist/hero-ecommerce-product-showcase.js +103 -81
  129. package/dist/hero-floating-images.cjs +1 -1
  130. package/dist/hero-floating-images.js +1 -1
  131. package/dist/hero-hiring-animated-text.cjs +4 -4
  132. package/dist/hero-hiring-animated-text.js +4 -4
  133. package/dist/hero-minimal-centered-dark.cjs +111 -82
  134. package/dist/hero-minimal-centered-dark.d.cts +1 -1
  135. package/dist/hero-minimal-centered-dark.d.ts +1 -1
  136. package/dist/hero-minimal-centered-dark.js +111 -82
  137. package/dist/hero-mobile-app-download.cjs +1 -1
  138. package/dist/hero-mobile-app-download.js +1 -1
  139. package/dist/hero-overlay-cta-grid.cjs +1 -1
  140. package/dist/hero-overlay-cta-grid.js +1 -1
  141. package/dist/hero-spiral-pattern-cards.cjs +1 -1
  142. package/dist/hero-spiral-pattern-cards.js +1 -1
  143. package/dist/hero-startup-launch-cta.cjs +1 -1
  144. package/dist/hero-startup-launch-cta.js +1 -1
  145. package/dist/hero-stats-social-proof.cjs +106 -90
  146. package/dist/hero-stats-social-proof.js +106 -90
  147. package/dist/hero-testimonial-image-grid.cjs +1 -1
  148. package/dist/hero-testimonial-image-grid.js +1 -1
  149. package/dist/hero-therapy-testimonial-grid.cjs +1 -1
  150. package/dist/hero-therapy-testimonial-grid.js +1 -1
  151. package/dist/hero-ui-library-showcase.cjs +63 -15
  152. package/dist/hero-ui-library-showcase.d.cts +5 -1
  153. package/dist/hero-ui-library-showcase.d.ts +5 -1
  154. package/dist/hero-ui-library-showcase.js +63 -15
  155. package/dist/index.cjs +44 -6
  156. package/dist/index.d.cts +3 -2
  157. package/dist/index.d.ts +3 -2
  158. package/dist/index.js +44 -6
  159. package/dist/link-page-newsletter-social.d.cts +1 -1
  160. package/dist/link-page-newsletter-social.d.ts +1 -1
  161. package/dist/offer-modal-membership-image.d.cts +1 -1
  162. package/dist/offer-modal-membership-image.d.ts +1 -1
  163. package/dist/offer-modal-newsletter-discount.d.cts +1 -1
  164. package/dist/offer-modal-newsletter-discount.d.ts +1 -1
  165. package/dist/offer-modal-sheet-newsletter.d.cts +1 -1
  166. package/dist/offer-modal-sheet-newsletter.d.ts +1 -1
  167. package/dist/registry.cjs +14465 -14767
  168. package/dist/registry.js +12664 -12966
  169. package/dist/resource-list-hero-filter.d.cts +1 -1
  170. package/dist/resource-list-hero-filter.d.ts +1 -1
  171. package/package.json +3 -3
@@ -3,14 +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');
11
- var CheckboxPrimitive = require('@radix-ui/react-checkbox');
12
- var LabelPrimitive = require('@radix-ui/react-label');
13
- var SeparatorPrimitive = require('@radix-ui/react-separator');
10
+ var inputs = require('@page-speed/forms/inputs');
14
11
  var integration = require('@page-speed/forms/integration');
15
12
 
16
13
  function _interopNamespace(e) {
@@ -32,13 +29,8 @@ function _interopNamespace(e) {
32
29
  }
33
30
 
34
31
  var React__namespace = /*#__PURE__*/_interopNamespace(React);
35
- var CheckboxPrimitive__namespace = /*#__PURE__*/_interopNamespace(CheckboxPrimitive);
36
- var LabelPrimitive__namespace = /*#__PURE__*/_interopNamespace(LabelPrimitive);
37
- var SeparatorPrimitive__namespace = /*#__PURE__*/_interopNamespace(SeparatorPrimitive);
38
32
 
39
33
  // components/blocks/contact/contact-catering.tsx
40
- var TextInput = inputs.TextInput;
41
- var TextArea = inputs.TextArea;
42
34
  function cn(...inputs) {
43
35
  return tailwindMerge.twMerge(clsx.clsx(inputs));
44
36
  }
@@ -385,6 +377,7 @@ var Pressable = React__namespace.forwardRef(
385
377
  rel,
386
378
  linkType,
387
379
  isInternal,
380
+ isExternal,
388
381
  handleClick
389
382
  } = navigation;
390
383
  const shouldRenderLink = normalizedHref && linkType !== "none";
@@ -460,111 +453,6 @@ var Pressable = React__namespace.forwardRef(
460
453
  }
461
454
  );
462
455
  Pressable.displayName = "Pressable";
463
- var svgCache = /* @__PURE__ */ new Map();
464
- function DynamicIcon({
465
- name,
466
- size = 28,
467
- color,
468
- className,
469
- alt
470
- }) {
471
- const [svgContent, setSvgContent] = React__namespace.useState(null);
472
- const [isLoading, setIsLoading] = React__namespace.useState(true);
473
- const [error, setError] = React__namespace.useState(null);
474
- const { url, iconName } = React__namespace.useMemo(() => {
475
- const separator = name.includes("/") ? "/" : ":";
476
- const [prefix, iconName2] = name.split(separator);
477
- const baseUrl = `https://icons.opensite.ai/api/icon/${prefix}/${iconName2}?format=svg&width=${size}&height=${size}&key=au382bi7fsh96w9h9xlrnat2jglx`;
478
- return {
479
- url: baseUrl,
480
- iconName: iconName2
481
- };
482
- }, [name, size]);
483
- React__namespace.useEffect(() => {
484
- let isMounted = true;
485
- const fetchSvg = async () => {
486
- const cached = svgCache.get(url);
487
- if (cached) {
488
- if (isMounted) {
489
- setSvgContent(cached);
490
- setIsLoading(false);
491
- }
492
- return;
493
- }
494
- try {
495
- setIsLoading(true);
496
- setError(null);
497
- const response = await fetch(url);
498
- if (!response.ok) {
499
- throw new Error(`Failed to fetch icon: ${response.status}`);
500
- }
501
- let svg = await response.text();
502
- svg = processSvgForCurrentColor(svg);
503
- svgCache.set(url, svg);
504
- if (isMounted) {
505
- setSvgContent(svg);
506
- setIsLoading(false);
507
- }
508
- } catch (err) {
509
- if (isMounted) {
510
- setError(err instanceof Error ? err.message : "Failed to load icon");
511
- setIsLoading(false);
512
- }
513
- }
514
- };
515
- fetchSvg();
516
- return () => {
517
- isMounted = false;
518
- };
519
- }, [url]);
520
- if (isLoading) {
521
- return /* @__PURE__ */ jsxRuntime.jsx(
522
- "span",
523
- {
524
- className: cn("inline-block", className),
525
- style: { width: size, height: size },
526
- "aria-hidden": "true"
527
- }
528
- );
529
- }
530
- if (error || !svgContent) {
531
- return /* @__PURE__ */ jsxRuntime.jsx(
532
- "span",
533
- {
534
- className: cn("inline-block", className),
535
- style: { width: size, height: size },
536
- role: "img",
537
- "aria-label": alt || iconName
538
- }
539
- );
540
- }
541
- return /* @__PURE__ */ jsxRuntime.jsx(
542
- "span",
543
- {
544
- className: cn("inline-flex items-center justify-center", className),
545
- style: {
546
- width: size,
547
- height: size,
548
- color: color || "inherit"
549
- },
550
- role: "img",
551
- "aria-label": alt || iconName,
552
- dangerouslySetInnerHTML: { __html: svgContent }
553
- }
554
- );
555
- }
556
- function processSvgForCurrentColor(svg) {
557
- let processed = svg;
558
- processed = processed.replace(
559
- /stroke=["'](#000000|#000|black)["']/gi,
560
- 'stroke="currentColor"'
561
- );
562
- processed = processed.replace(
563
- /fill=["'](#000000|#000|black)["']/gi,
564
- 'fill="currentColor"'
565
- );
566
- return processed;
567
- }
568
456
  function Card({ className, ...props }) {
569
457
  return /* @__PURE__ */ jsxRuntime.jsx(
570
458
  "div",
@@ -588,66 +476,371 @@ function CardContent({ className, ...props }) {
588
476
  }
589
477
  );
590
478
  }
591
- function Checkbox({
479
+ function DynamicFormField({
480
+ field,
592
481
  className,
593
- ...props
482
+ uploadProgress = {},
483
+ onFileUpload,
484
+ onFileRemove,
485
+ isUploading = false
594
486
  }) {
487
+ const fieldId = field.name;
488
+ const usesGroupLegend = field.type === "radio" || field.type === "checkbox-group";
489
+ const usesInlineCheckboxLabel = field.type === "checkbox";
490
+ const shouldRenderFieldLabel = !usesGroupLegend && !usesInlineCheckboxLabel;
491
+ const checkboxLabel = /* @__PURE__ */ jsxRuntime.jsxs(jsxRuntime.Fragment, { children: [
492
+ field.label,
493
+ field.required ? /* @__PURE__ */ jsxRuntime.jsx("span", { className: "text-destructive ml-1", children: "*" }) : null
494
+ ] });
595
495
  return /* @__PURE__ */ jsxRuntime.jsx(
596
- CheckboxPrimitive__namespace.Root,
496
+ forms.Field,
597
497
  {
598
- "data-slot": "checkbox",
599
- className: cn(
600
- "peer border-input dark:bg-input/30 data-[state=checked]:bg-primary data-[state=checked]:text-primary-foreground dark:data-[state=checked]:bg-primary data-[state=checked]:border-primary focus-visible:border-ring focus-visible:ring-ring/50 aria-invalid:ring-destructive/20 dark:aria-invalid:ring-destructive/40 aria-invalid:border-destructive size-4 shrink-0 rounded-[4px] border shadow-xs transition-shadow outline-none focus-visible:ring-[3px] disabled:cursor-not-allowed disabled:opacity-50",
601
- className
602
- ),
603
- ...props,
604
- children: /* @__PURE__ */ jsxRuntime.jsx(
605
- CheckboxPrimitive__namespace.Indicator,
606
- {
607
- "data-slot": "checkbox-indicator",
608
- className: "grid place-content-center text-current transition-none",
609
- children: /* @__PURE__ */ jsxRuntime.jsx(DynamicIcon, { name: "lucide/check", size: 14 })
610
- }
611
- )
498
+ name: field.name,
499
+ label: shouldRenderFieldLabel ? field.label : void 0,
500
+ description: shouldRenderFieldLabel ? field.description : void 0,
501
+ required: field.required,
502
+ className: cn("space-y-2", className),
503
+ children: ({ field: formField, meta }) => /* @__PURE__ */ jsxRuntime.jsxs("div", { children: [
504
+ (field.type === "text" || field.type === "email" || field.type === "tel" || field.type === "search" || field.type === "password" || field.type === "url") && /* @__PURE__ */ jsxRuntime.jsx(
505
+ inputs.TextInput,
506
+ {
507
+ ...formField,
508
+ id: fieldId,
509
+ type: field.type,
510
+ placeholder: field.placeholder,
511
+ error: meta.touched && !!meta.error,
512
+ disabled: field.disabled,
513
+ "aria-label": field.label
514
+ }
515
+ ),
516
+ field.type === "number" && /* @__PURE__ */ jsxRuntime.jsx(
517
+ inputs.TextInput,
518
+ {
519
+ ...formField,
520
+ id: fieldId,
521
+ type: "text",
522
+ placeholder: field.placeholder,
523
+ error: meta.touched && !!meta.error,
524
+ disabled: field.disabled,
525
+ "aria-label": field.label
526
+ }
527
+ ),
528
+ field.type === "textarea" && /* @__PURE__ */ jsxRuntime.jsx(
529
+ inputs.TextArea,
530
+ {
531
+ ...formField,
532
+ id: fieldId,
533
+ placeholder: field.placeholder,
534
+ rows: field.rows || 4,
535
+ error: meta.touched && !!meta.error,
536
+ disabled: field.disabled,
537
+ "aria-label": field.label
538
+ }
539
+ ),
540
+ field.type === "select" && field.options && /* @__PURE__ */ jsxRuntime.jsx(
541
+ inputs.Select,
542
+ {
543
+ ...formField,
544
+ id: fieldId,
545
+ options: field.options,
546
+ placeholder: field.placeholder || `Select ${field.label.toLowerCase()}`,
547
+ error: meta.touched && !!meta.error,
548
+ disabled: field.disabled,
549
+ "aria-label": field.label
550
+ }
551
+ ),
552
+ field.type === "multi-select" && field.options && /* @__PURE__ */ jsxRuntime.jsx(
553
+ inputs.MultiSelect,
554
+ {
555
+ ...formField,
556
+ id: fieldId,
557
+ options: field.options,
558
+ placeholder: field.placeholder || `Select ${field.label.toLowerCase()}`,
559
+ error: meta.touched && !!meta.error,
560
+ disabled: field.disabled,
561
+ "aria-label": field.label
562
+ }
563
+ ),
564
+ field.type === "radio" && field.options && /* @__PURE__ */ jsxRuntime.jsx(
565
+ inputs.Radio,
566
+ {
567
+ ...formField,
568
+ id: fieldId,
569
+ options: field.options,
570
+ label: field.label,
571
+ description: field.description,
572
+ required: field.required,
573
+ disabled: field.disabled,
574
+ layout: field.layout || "stacked",
575
+ error: meta.touched && !!meta.error,
576
+ "aria-label": field.label
577
+ }
578
+ ),
579
+ field.type === "checkbox" && /* @__PURE__ */ jsxRuntime.jsx(
580
+ inputs.Checkbox,
581
+ {
582
+ ...formField,
583
+ id: fieldId,
584
+ value: formField.value === true || formField.value === "true",
585
+ onChange: (checked) => formField.onChange(checked),
586
+ label: checkboxLabel,
587
+ description: field.description,
588
+ disabled: field.disabled,
589
+ required: field.required,
590
+ error: meta.touched && !!meta.error,
591
+ "aria-label": field.label
592
+ }
593
+ ),
594
+ field.type === "checkbox-group" && field.options && /* @__PURE__ */ jsxRuntime.jsx(
595
+ inputs.CheckboxGroup,
596
+ {
597
+ ...formField,
598
+ id: fieldId,
599
+ options: field.options,
600
+ label: field.label,
601
+ description: field.description,
602
+ required: field.required,
603
+ disabled: field.disabled,
604
+ layout: field.layout || "stacked",
605
+ error: meta.touched && !!meta.error,
606
+ "aria-label": field.label
607
+ }
608
+ ),
609
+ (field.type === "date-picker" || field.type === "date") && /* @__PURE__ */ jsxRuntime.jsx(
610
+ inputs.DatePicker,
611
+ {
612
+ ...formField,
613
+ id: fieldId,
614
+ placeholder: field.placeholder,
615
+ error: meta.touched && !!meta.error,
616
+ disabled: field.disabled,
617
+ "aria-label": field.label
618
+ }
619
+ ),
620
+ field.type === "date-range" && /* @__PURE__ */ jsxRuntime.jsx(
621
+ inputs.DateRangePicker,
622
+ {
623
+ ...formField,
624
+ id: fieldId,
625
+ placeholder: field.placeholder,
626
+ error: meta.touched && !!meta.error,
627
+ disabled: field.disabled,
628
+ "aria-label": field.label
629
+ }
630
+ ),
631
+ field.type === "time" && /* @__PURE__ */ jsxRuntime.jsx(
632
+ inputs.TimePicker,
633
+ {
634
+ ...formField,
635
+ id: fieldId,
636
+ placeholder: field.placeholder,
637
+ error: meta.touched && !!meta.error,
638
+ disabled: field.disabled,
639
+ "aria-label": field.label
640
+ }
641
+ ),
642
+ field.type === "file" && /* @__PURE__ */ jsxRuntime.jsx(
643
+ inputs.FileInput,
644
+ {
645
+ ...formField,
646
+ id: fieldId,
647
+ accept: field.accept,
648
+ maxSize: field.maxSize || 5 * 1024 * 1024,
649
+ maxFiles: field.maxFiles || 1,
650
+ multiple: field.multiple || false,
651
+ placeholder: field.placeholder || "Choose file(s)...",
652
+ error: meta.touched && !!meta.error,
653
+ disabled: field.disabled || isUploading,
654
+ showProgress: true,
655
+ uploadProgress,
656
+ onChange: (files) => {
657
+ formField.onChange(files);
658
+ if (files.length > 0 && onFileUpload) {
659
+ onFileUpload(files);
660
+ }
661
+ },
662
+ onFileRemove,
663
+ "aria-label": field.label
664
+ }
665
+ ),
666
+ field.type === "rich-text" && /* @__PURE__ */ jsxRuntime.jsx(
667
+ inputs.RichTextEditor,
668
+ {
669
+ ...formField,
670
+ id: fieldId,
671
+ placeholder: field.placeholder,
672
+ error: meta.touched && !!meta.error,
673
+ disabled: field.disabled,
674
+ "aria-label": field.label
675
+ }
676
+ )
677
+ ] })
612
678
  }
613
679
  );
614
680
  }
615
- function Label({
616
- className,
617
- ...props
618
- }) {
619
- return /* @__PURE__ */ jsxRuntime.jsx(
620
- LabelPrimitive__namespace.Root,
621
- {
622
- "data-slot": "label",
623
- className: cn(
624
- "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",
625
- className
626
- ),
627
- ...props
628
- }
681
+
682
+ // lib/form-field-types.ts
683
+ function generateInitialValues(fields) {
684
+ return fields.reduce(
685
+ (acc, field) => {
686
+ if (field.type === "checkbox") {
687
+ acc[field.name] = false;
688
+ } else if (field.type === "checkbox-group" || field.type === "multi-select") {
689
+ acc[field.name] = [];
690
+ } else if (field.type === "file") {
691
+ acc[field.name] = [];
692
+ } else if (field.type === "date-range") {
693
+ acc[field.name] = { start: null, end: null };
694
+ } else {
695
+ acc[field.name] = "";
696
+ }
697
+ return acc;
698
+ },
699
+ {}
629
700
  );
630
701
  }
631
- function Separator({
632
- className,
633
- orientation = "horizontal",
634
- decorative = true,
635
- ...props
636
- }) {
637
- return /* @__PURE__ */ jsxRuntime.jsx(
638
- SeparatorPrimitive__namespace.Root,
639
- {
640
- "data-slot": "separator",
641
- decorative,
642
- orientation,
643
- className: cn(
644
- "bg-border shrink-0 data-[orientation=horizontal]:h-px data-[orientation=horizontal]:w-full data-[orientation=vertical]:h-full data-[orientation=vertical]:w-px",
645
- className
646
- ),
647
- ...props
648
- }
702
+ function generateValidationSchema(fields) {
703
+ return fields.reduce(
704
+ (acc, field) => {
705
+ acc[field.name] = (value, allValues) => {
706
+ if (field.required) {
707
+ if (!value || typeof value === "string" && !value.trim()) {
708
+ return `${field.label} is required`;
709
+ }
710
+ }
711
+ if (field.type === "email" && value) {
712
+ if (!/^[^\s@]+@[^\s@]+\.[^\s@]+$/.test(value)) {
713
+ return "Please enter a valid email address";
714
+ }
715
+ }
716
+ if (field.type === "url" && value) {
717
+ try {
718
+ new URL(value);
719
+ } catch {
720
+ return "Please enter a valid URL";
721
+ }
722
+ }
723
+ if (field.validator) {
724
+ return field.validator(value, allValues);
725
+ }
726
+ return void 0;
727
+ };
728
+ return acc;
729
+ },
730
+ {}
649
731
  );
650
732
  }
733
+ function getColumnSpanClass(span) {
734
+ if (!span || span === 12) return "col-span-12";
735
+ return `col-span-12 sm:col-span-${Math.min(span, 12)}`;
736
+ }
737
+ function useContactForm(options) {
738
+ const {
739
+ formFields,
740
+ formConfig,
741
+ onSubmit,
742
+ onSuccess,
743
+ onError,
744
+ resetOnSuccess = true,
745
+ uploadTokens = []
746
+ } = options;
747
+ const [submissionError, setSubmissionError] = React.useState(null);
748
+ const submissionConfig = formConfig?.submissionConfig;
749
+ const redirectUrl = submissionConfig?.redirectUrl;
750
+ const redirectNavigation = useNavigation({ href: redirectUrl });
751
+ const resetSubmissionState = React.useCallback(() => {
752
+ setSubmissionError(null);
753
+ }, []);
754
+ const performRedirect = React.useCallback(() => {
755
+ if (!redirectUrl || typeof window === "undefined") {
756
+ return;
757
+ }
758
+ const navigate = () => {
759
+ if (redirectNavigation.shouldUseRouter && redirectNavigation.normalizedHref) {
760
+ const handler = window.__opensiteNavigationHandler;
761
+ if (typeof handler === "function") {
762
+ try {
763
+ const handled = handler(redirectNavigation.normalizedHref, void 0);
764
+ if (handled !== false) {
765
+ return;
766
+ }
767
+ } catch (error) {
768
+ console.error("Internal redirect handler failed:", error);
769
+ }
770
+ }
771
+ }
772
+ const destination = redirectNavigation.normalizedHref || redirectUrl;
773
+ window.location.assign(destination);
774
+ };
775
+ window.setTimeout(navigate, 150);
776
+ }, [redirectNavigation, redirectUrl]);
777
+ const form = forms.useForm({
778
+ initialValues: React.useMemo(
779
+ () => generateInitialValues(formFields),
780
+ [formFields]
781
+ ),
782
+ validationSchema: React.useMemo(
783
+ () => generateValidationSchema(formFields),
784
+ [formFields]
785
+ ),
786
+ onSubmit: async (values, helpers) => {
787
+ resetSubmissionState();
788
+ const shouldAutoSubmit = Boolean(formConfig?.endpoint);
789
+ if (!shouldAutoSubmit && !onSubmit) {
790
+ return;
791
+ }
792
+ try {
793
+ let result;
794
+ const submissionValues = {
795
+ ...values,
796
+ ...uploadTokens.length > 0 && {
797
+ contact_form_upload_tokens: uploadTokens
798
+ }
799
+ };
800
+ if (shouldAutoSubmit) {
801
+ result = await submitPageSpeedForm(submissionValues, formConfig);
802
+ }
803
+ if (onSubmit) {
804
+ await onSubmit(submissionValues);
805
+ }
806
+ if (shouldAutoSubmit || onSubmit) {
807
+ try {
808
+ await submissionConfig?.handleFormSubmission?.({
809
+ formData: submissionValues,
810
+ responseData: result
811
+ });
812
+ } catch (callbackError) {
813
+ console.error("handleFormSubmission callback failed:", callbackError);
814
+ }
815
+ if (resetOnSuccess) {
816
+ helpers.resetForm();
817
+ }
818
+ onSuccess?.(result);
819
+ if (submissionConfig?.behavior === "redirect" && submissionConfig.redirectUrl) {
820
+ performRedirect();
821
+ }
822
+ }
823
+ } catch (error) {
824
+ if (error instanceof PageSpeedFormSubmissionError && error.formErrors) {
825
+ helpers.setErrors(error.formErrors);
826
+ }
827
+ const errorMessage = error instanceof Error ? error.message : "Form submission failed";
828
+ setSubmissionError(errorMessage);
829
+ onError?.(error);
830
+ }
831
+ }
832
+ });
833
+ const formMethod = formConfig?.method?.toLowerCase() === "get" ? "get" : "post";
834
+ return {
835
+ form,
836
+ isSubmitted: form.status === "success",
837
+ submissionError,
838
+ formMethod,
839
+ resetSubmissionState
840
+ };
841
+ }
842
+
843
+ // lib/forms.ts
651
844
  var PageSpeedFormSubmissionError = class extends Error {
652
845
  constructor(message, options = {}) {
653
846
  super(message);
@@ -1159,19 +1352,19 @@ var SERVICE_STYLES = [
1159
1352
  { value: "cocktail", label: "Cocktail", description: "Passed appetizers" }
1160
1353
  ];
1161
1354
  var CUISINES = [
1162
- { id: "american", label: "American" },
1163
- { id: "italian", label: "Italian" },
1164
- { id: "asian", label: "Asian Fusion" },
1165
- { id: "mexican", label: "Mexican" },
1166
- { id: "mediterranean", label: "Mediterranean" },
1167
- { id: "bbq", label: "BBQ" }
1355
+ { value: "american", label: "American" },
1356
+ { value: "italian", label: "Italian" },
1357
+ { value: "asian", label: "Asian Fusion" },
1358
+ { value: "mexican", label: "Mexican" },
1359
+ { value: "mediterranean", label: "Mediterranean" },
1360
+ { value: "bbq", label: "BBQ" }
1168
1361
  ];
1169
1362
  var DIETARY_OPTIONS = [
1170
- { id: "vegetarian", label: "Vegetarian options" },
1171
- { id: "vegan", label: "Vegan options" },
1172
- { id: "gluten-free", label: "Gluten-free options" },
1173
- { id: "kosher", label: "Kosher" },
1174
- { id: "halal", label: "Halal" }
1363
+ { value: "vegetarian", label: "Vegetarian options" },
1364
+ { value: "vegan", label: "Vegan options" },
1365
+ { value: "gluten-free", label: "Gluten-free options" },
1366
+ { value: "kosher", label: "Kosher" },
1367
+ { value: "halal", label: "Halal" }
1175
1368
  ];
1176
1369
  var GUEST_COUNTS = [
1177
1370
  { value: "10-25", label: "10-25 guests" },
@@ -1191,13 +1384,124 @@ var BUDGET_RANGES = [
1191
1384
  { value: "100-150", label: "$100-150 / person" },
1192
1385
  { value: "150+", label: "$150+ / person" }
1193
1386
  ];
1387
+ var DEFAULT_FORM_FIELDS = [
1388
+ {
1389
+ name: "eventType",
1390
+ type: "select",
1391
+ label: "Event Type",
1392
+ placeholder: "Select event type",
1393
+ required: true,
1394
+ columnSpan: 6,
1395
+ options: EVENT_TYPES
1396
+ },
1397
+ {
1398
+ name: "eventDate",
1399
+ type: "date",
1400
+ label: "Event Date",
1401
+ placeholder: "Select date",
1402
+ required: true,
1403
+ columnSpan: 6
1404
+ },
1405
+ {
1406
+ name: "guestCount",
1407
+ type: "select",
1408
+ label: "Number of Guests",
1409
+ placeholder: "Select guest count",
1410
+ required: true,
1411
+ columnSpan: 6,
1412
+ options: GUEST_COUNTS
1413
+ },
1414
+ {
1415
+ name: "budget",
1416
+ type: "select",
1417
+ label: "Budget Per Person",
1418
+ placeholder: "Select budget range",
1419
+ required: false,
1420
+ columnSpan: 6,
1421
+ options: BUDGET_RANGES
1422
+ },
1423
+ {
1424
+ name: "serviceStyle",
1425
+ type: "radio",
1426
+ label: "Service Style",
1427
+ required: true,
1428
+ columnSpan: 12,
1429
+ options: SERVICE_STYLES
1430
+ },
1431
+ {
1432
+ name: "cuisinePreferences",
1433
+ type: "checkbox-group",
1434
+ label: "Cuisine Preferences",
1435
+ required: false,
1436
+ columnSpan: 12,
1437
+ options: CUISINES
1438
+ },
1439
+ {
1440
+ name: "dietaryAccommodations",
1441
+ type: "checkbox-group",
1442
+ label: "Dietary Accommodations",
1443
+ required: false,
1444
+ columnSpan: 12,
1445
+ options: DIETARY_OPTIONS
1446
+ },
1447
+ {
1448
+ name: "name",
1449
+ type: "text",
1450
+ label: "Full Name",
1451
+ placeholder: "John Doe",
1452
+ required: true,
1453
+ columnSpan: 12
1454
+ },
1455
+ {
1456
+ name: "email",
1457
+ type: "email",
1458
+ label: "Email Address",
1459
+ placeholder: "john@example.com",
1460
+ required: true,
1461
+ columnSpan: 6
1462
+ },
1463
+ {
1464
+ name: "phone",
1465
+ type: "tel",
1466
+ label: "Phone Number",
1467
+ placeholder: "+1 (555) 000-0000",
1468
+ required: true,
1469
+ columnSpan: 6
1470
+ },
1471
+ {
1472
+ name: "venue",
1473
+ type: "text",
1474
+ label: "Venue / Location",
1475
+ placeholder: "Event venue or address",
1476
+ required: false,
1477
+ columnSpan: 12
1478
+ },
1479
+ {
1480
+ name: "details",
1481
+ type: "textarea",
1482
+ label: "Additional Details",
1483
+ placeholder: "Tell us more about your event...",
1484
+ required: false,
1485
+ rows: 4,
1486
+ columnSpan: 12
1487
+ },
1488
+ {
1489
+ name: "tasting",
1490
+ type: "checkbox",
1491
+ label: "I'm interested in scheduling a tasting",
1492
+ required: false,
1493
+ columnSpan: 12
1494
+ }
1495
+ ];
1194
1496
  function ContactCatering({
1195
1497
  heading,
1196
1498
  description,
1197
- buttonText,
1499
+ buttonText = "Request Quote",
1198
1500
  buttonIcon,
1199
1501
  actions,
1200
1502
  actionsSlot,
1503
+ formFields,
1504
+ successMessage = "Thank you for your inquiry! We'll get back to you within 24 hours with a custom proposal.",
1201
1505
  className,
1202
1506
  spacing = "py-8 md:py-32",
1203
1507
  containerClassName = "px-6 sm:px-6 md:px-8 lg:px-8",
@@ -1207,6 +1511,8 @@ function ContactCatering({
1207
1511
  cardClassName,
1208
1512
  cardContentClassName,
1209
1513
  formClassName,
1514
+ successMessageClassName,
1515
+ errorMessageClassName,
1210
1516
  submitClassName,
1211
1517
  background,
1212
1518
  pattern,
@@ -1216,76 +1522,17 @@ function ContactCatering({
1216
1522
  onSuccess,
1217
1523
  onError
1218
1524
  }) {
1219
- const form = forms.useForm({
1220
- initialValues: {
1221
- eventType: "",
1222
- eventDate: "",
1223
- guestCount: "",
1224
- startTime: "",
1225
- endTime: "",
1226
- venue: "",
1227
- serviceStyle: "buffet",
1228
- cuisinePreferences: [],
1229
- dietaryAccommodations: [],
1230
- budget: "",
1231
- name: "",
1232
- phone: "",
1233
- email: "",
1234
- details: "",
1235
- tasting: false
1236
- },
1237
- validationSchema: {
1238
- eventType: (value) => !value ? "Please select an event type" : void 0,
1239
- eventDate: (value) => !value ? "Event date is required" : void 0,
1240
- guestCount: (value) => !value ? "Please select guest count" : void 0,
1241
- name: (value) => !value ? "Name is required" : void 0,
1242
- email: (value) => {
1243
- if (!value) return "Email is required";
1244
- if (!/^[^\s@]+@[^\s@]+\.[^\s@]+$/.test(value))
1245
- return "Please enter a valid email address";
1246
- return void 0;
1247
- },
1248
- phone: (value) => !value ? "Phone number is required" : void 0
1249
- },
1250
- onSubmit: async (values, helpers) => {
1251
- const shouldAutoSubmit = Boolean(formConfig?.endpoint);
1252
- if (!shouldAutoSubmit && !onSubmit) {
1253
- return;
1254
- }
1255
- try {
1256
- let result;
1257
- if (shouldAutoSubmit) {
1258
- result = await submitPageSpeedForm(values, formConfig);
1259
- }
1260
- if (onSubmit) {
1261
- await onSubmit(values);
1262
- }
1263
- if (shouldAutoSubmit || onSubmit) {
1264
- if (formConfig?.resetOnSuccess !== false) {
1265
- helpers.resetForm();
1266
- }
1267
- onSuccess?.(result);
1268
- }
1269
- } catch (error) {
1270
- if (error instanceof PageSpeedFormSubmissionError && error.formErrors) {
1271
- helpers.setErrors(error.formErrors);
1272
- }
1273
- onError?.(error);
1274
- throw error;
1275
- }
1276
- }
1525
+ const fields = React.useMemo(
1526
+ () => formFields || DEFAULT_FORM_FIELDS,
1527
+ [formFields]
1528
+ );
1529
+ const { form, submissionError, formMethod, resetSubmissionState } = useContactForm({
1530
+ formFields: fields,
1531
+ formConfig,
1532
+ onSubmit,
1533
+ onSuccess,
1534
+ onError
1277
1535
  });
1278
- const formMethod = formConfig?.method?.toLowerCase() === "get" ? "get" : "post";
1279
- const toggleCuisinePreference = (value) => {
1280
- const current = form.values.cuisinePreferences;
1281
- const updated = current.includes(value) ? current.filter((v) => v !== value) : [...current, value];
1282
- form.setFieldValue("cuisinePreferences", updated);
1283
- };
1284
- const toggleDietaryAccommodation = (value) => {
1285
- const current = form.values.dietaryAccommodations;
1286
- const updated = current.includes(value) ? current.filter((v) => v !== value) : [...current, value];
1287
- form.setFieldValue("dietaryAccommodations", updated);
1288
- };
1289
1536
  const actionsContent = React__namespace.useMemo(() => {
1290
1537
  if (actionsSlot) return actionsSlot;
1291
1538
  if (actions && actions.length > 0) {
@@ -1354,256 +1601,22 @@ function ContactCatering({
1354
1601
  form,
1355
1602
  action: formConfig?.endpoint,
1356
1603
  method: formMethod,
1357
- className: cn("space-y-8", formClassName),
1604
+ submissionError,
1605
+ successMessage,
1606
+ successMessageClassName,
1607
+ errorMessageClassName,
1608
+ submissionConfig: formConfig?.submissionConfig,
1609
+ onNewSubmission: resetSubmissionState,
1610
+ className: cn("space-y-6", formClassName),
1358
1611
  children: [
1359
- /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "space-y-4", children: [
1360
- /* @__PURE__ */ jsxRuntime.jsx("h3", { className: "text-lg font-semibold", children: "Event Details" }),
1361
- /* @__PURE__ */ jsxRuntime.jsx(forms.Field, { name: "eventType", children: ({ field, meta }) => /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "space-y-2", children: [
1362
- /* @__PURE__ */ jsxRuntime.jsx(Label, { htmlFor: "event-type", children: "Event Type" }),
1363
- /* @__PURE__ */ jsxRuntime.jsxs(
1364
- inputs.Select,
1365
- {
1366
- ...field,
1367
- id: "event-type",
1368
- error: meta.touched && !!meta.error,
1369
- "aria-label": "Event Type",
1370
- children: [
1371
- /* @__PURE__ */ jsxRuntime.jsx("option", { value: "", children: "Select event type" }),
1372
- EVENT_TYPES.map((type) => /* @__PURE__ */ jsxRuntime.jsx("option", { value: type.value, children: type.label }, type.value))
1373
- ]
1374
- }
1375
- )
1376
- ] }) }),
1377
- /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "grid gap-4 sm:grid-cols-2", children: [
1378
- /* @__PURE__ */ jsxRuntime.jsx(forms.Field, { name: "guestCount", children: ({ field, meta }) => /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "space-y-2", children: [
1379
- /* @__PURE__ */ jsxRuntime.jsx(Label, { htmlFor: "guest-count", children: "Number of Guests" }),
1380
- /* @__PURE__ */ jsxRuntime.jsxs(
1381
- inputs.Select,
1382
- {
1383
- ...field,
1384
- id: "guest-count",
1385
- error: meta.touched && !!meta.error,
1386
- "aria-label": "Number of Guests",
1387
- children: [
1388
- /* @__PURE__ */ jsxRuntime.jsx("option", { value: "", children: "Select guest count" }),
1389
- GUEST_COUNTS.map((count) => /* @__PURE__ */ jsxRuntime.jsx("option", { value: count.value, children: count.label }, count.value))
1390
- ]
1391
- }
1392
- )
1393
- ] }) }),
1394
- /* @__PURE__ */ jsxRuntime.jsx(forms.Field, { name: "eventDate", children: ({ field, meta }) => /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "space-y-2", children: [
1395
- /* @__PURE__ */ jsxRuntime.jsx(Label, { htmlFor: "event-date", children: "Event Date" }),
1396
- /* @__PURE__ */ jsxRuntime.jsx(
1397
- TextInput,
1398
- {
1399
- ...field,
1400
- id: "event-date",
1401
- type: "date",
1402
- error: meta.touched && !!meta.error,
1403
- "aria-label": "Event Date"
1404
- }
1405
- )
1406
- ] }) })
1407
- ] }),
1408
- /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "grid gap-4 sm:grid-cols-2", children: [
1409
- /* @__PURE__ */ jsxRuntime.jsx(forms.Field, { name: "startTime", children: ({ field, meta }) => /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "space-y-2", children: [
1410
- /* @__PURE__ */ jsxRuntime.jsx(Label, { htmlFor: "start-time", children: "Start Time" }),
1411
- /* @__PURE__ */ jsxRuntime.jsx(
1412
- TextInput,
1413
- {
1414
- ...field,
1415
- id: "start-time",
1416
- type: "time",
1417
- error: meta.touched && !!meta.error,
1418
- "aria-label": "Start Time"
1419
- }
1420
- )
1421
- ] }) }),
1422
- /* @__PURE__ */ jsxRuntime.jsx(forms.Field, { name: "endTime", children: ({ field, meta }) => /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "space-y-2", children: [
1423
- /* @__PURE__ */ jsxRuntime.jsx(Label, { htmlFor: "end-time", children: "End Time (Optional)" }),
1424
- /* @__PURE__ */ jsxRuntime.jsx(
1425
- TextInput,
1426
- {
1427
- ...field,
1428
- id: "end-time",
1429
- type: "time",
1430
- error: meta.touched && !!meta.error,
1431
- "aria-label": "End Time"
1432
- }
1433
- )
1434
- ] }) })
1435
- ] }),
1436
- /* @__PURE__ */ jsxRuntime.jsx(forms.Field, { name: "venue", children: ({ field, meta }) => /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "space-y-2", children: [
1437
- /* @__PURE__ */ jsxRuntime.jsx(Label, { htmlFor: "venue", children: "Venue / Location" }),
1438
- /* @__PURE__ */ jsxRuntime.jsx(
1439
- TextInput,
1440
- {
1441
- ...field,
1442
- id: "venue",
1443
- placeholder: "Event venue or location",
1444
- error: meta.touched && !!meta.error,
1445
- "aria-label": "Venue"
1446
- }
1447
- )
1448
- ] }) })
1449
- ] }),
1450
- /* @__PURE__ */ jsxRuntime.jsx(Separator, {}),
1451
- /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "space-y-4", children: [
1452
- /* @__PURE__ */ jsxRuntime.jsx("h3", { className: "text-lg font-semibold", children: "Service Preferences" }),
1453
- /* @__PURE__ */ jsxRuntime.jsx(forms.Field, { name: "serviceStyle", children: ({ field }) => /* @__PURE__ */ jsxRuntime.jsx(
1454
- inputs.Radio,
1455
- {
1456
- name: "serviceStyle",
1457
- label: "Service Style",
1458
- value: field.value,
1459
- onChange: field.onChange,
1460
- options: SERVICE_STYLES,
1461
- layout: "stacked",
1462
- className: "space-y-2"
1463
- }
1464
- ) }),
1465
- /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "space-y-3", children: [
1466
- /* @__PURE__ */ jsxRuntime.jsx(Label, { children: "Cuisine Preferences (Optional)" }),
1467
- /* @__PURE__ */ jsxRuntime.jsx("div", { className: "grid gap-3 sm:grid-cols-2 md:grid-cols-3", children: CUISINES.map((cuisine) => /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "flex items-center gap-2", children: [
1468
- /* @__PURE__ */ jsxRuntime.jsx(
1469
- Checkbox,
1470
- {
1471
- id: cuisine.id,
1472
- checked: form.values.cuisinePreferences.includes(
1473
- cuisine.id
1474
- ),
1475
- onCheckedChange: () => toggleCuisinePreference(cuisine.id)
1476
- }
1477
- ),
1478
- /* @__PURE__ */ jsxRuntime.jsx(
1479
- Label,
1480
- {
1481
- htmlFor: cuisine.id,
1482
- className: "cursor-pointer font-normal",
1483
- children: cuisine.label
1484
- }
1485
- )
1486
- ] }, cuisine.id)) })
1487
- ] }),
1488
- /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "space-y-3", children: [
1489
- /* @__PURE__ */ jsxRuntime.jsx(Label, { children: "Dietary Accommodations (Optional)" }),
1490
- /* @__PURE__ */ jsxRuntime.jsx("div", { className: "grid gap-3 sm:grid-cols-2", children: DIETARY_OPTIONS.map((option) => /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "flex items-center gap-2", children: [
1491
- /* @__PURE__ */ jsxRuntime.jsx(
1492
- Checkbox,
1493
- {
1494
- id: option.id,
1495
- checked: form.values.dietaryAccommodations.includes(
1496
- option.id
1497
- ),
1498
- onCheckedChange: () => toggleDietaryAccommodation(option.id)
1499
- }
1500
- ),
1501
- /* @__PURE__ */ jsxRuntime.jsx(
1502
- Label,
1503
- {
1504
- htmlFor: option.id,
1505
- className: "cursor-pointer font-normal",
1506
- children: option.label
1507
- }
1508
- )
1509
- ] }, option.id)) })
1510
- ] }),
1511
- /* @__PURE__ */ jsxRuntime.jsx(forms.Field, { name: "budget", children: ({ field, meta }) => /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "space-y-2", children: [
1512
- /* @__PURE__ */ jsxRuntime.jsx(Label, { htmlFor: "budget", children: "Budget Per Person (Optional)" }),
1513
- /* @__PURE__ */ jsxRuntime.jsxs(
1514
- inputs.Select,
1515
- {
1516
- ...field,
1517
- id: "budget",
1518
- error: meta.touched && !!meta.error,
1519
- "aria-label": "Budget Per Person",
1520
- children: [
1521
- /* @__PURE__ */ jsxRuntime.jsx("option", { value: "", children: "Select budget range" }),
1522
- BUDGET_RANGES.map((range) => /* @__PURE__ */ jsxRuntime.jsx("option", { value: range.value, children: range.label }, range.value))
1523
- ]
1524
- }
1525
- )
1526
- ] }) })
1527
- ] }),
1528
- /* @__PURE__ */ jsxRuntime.jsx(Separator, {}),
1529
- /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "space-y-4", children: [
1530
- /* @__PURE__ */ jsxRuntime.jsx("h3", { className: "text-lg font-semibold", children: "Your Information" }),
1531
- /* @__PURE__ */ jsxRuntime.jsx(forms.Field, { name: "name", children: ({ field, meta }) => /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "space-y-2", children: [
1532
- /* @__PURE__ */ jsxRuntime.jsx(Label, { htmlFor: "name", children: "Full Name" }),
1533
- /* @__PURE__ */ jsxRuntime.jsx(
1534
- TextInput,
1535
- {
1536
- ...field,
1537
- id: "name",
1538
- placeholder: "John Doe",
1539
- error: meta.touched && !!meta.error,
1540
- "aria-label": "Full Name"
1541
- }
1542
- )
1543
- ] }) }),
1544
- /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "grid gap-4 sm:grid-cols-2", children: [
1545
- /* @__PURE__ */ jsxRuntime.jsx(forms.Field, { name: "email", children: ({ field, meta }) => /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "space-y-2", children: [
1546
- /* @__PURE__ */ jsxRuntime.jsx(Label, { htmlFor: "email", children: "Email" }),
1547
- /* @__PURE__ */ jsxRuntime.jsx(
1548
- TextInput,
1549
- {
1550
- ...field,
1551
- id: "email",
1552
- type: "email",
1553
- placeholder: "john@example.com",
1554
- error: meta.touched && !!meta.error,
1555
- "aria-label": "Email"
1556
- }
1557
- )
1558
- ] }) }),
1559
- /* @__PURE__ */ jsxRuntime.jsx(forms.Field, { name: "phone", children: ({ field, meta }) => /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "space-y-2", children: [
1560
- /* @__PURE__ */ jsxRuntime.jsx(Label, { htmlFor: "phone", children: "Phone" }),
1561
- /* @__PURE__ */ jsxRuntime.jsx(
1562
- TextInput,
1563
- {
1564
- ...field,
1565
- id: "phone",
1566
- type: "tel",
1567
- placeholder: "+1 (555) 000-0000",
1568
- error: meta.touched && !!meta.error,
1569
- "aria-label": "Phone"
1570
- }
1571
- )
1572
- ] }) })
1573
- ] }),
1574
- /* @__PURE__ */ jsxRuntime.jsx(forms.Field, { name: "details", children: ({ field, meta }) => /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "space-y-2", children: [
1575
- /* @__PURE__ */ jsxRuntime.jsx(Label, { htmlFor: "details", children: "Additional Details (Optional)" }),
1576
- /* @__PURE__ */ jsxRuntime.jsx(
1577
- TextArea,
1578
- {
1579
- ...field,
1580
- id: "details",
1581
- placeholder: "Tell us about your menu preferences, budget, or any special requirements...",
1582
- rows: 4,
1583
- error: meta.touched && !!meta.error,
1584
- "aria-label": "Additional Details"
1585
- }
1586
- )
1587
- ] }) }),
1588
- /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "flex items-center gap-2", children: [
1589
- /* @__PURE__ */ jsxRuntime.jsx(
1590
- Checkbox,
1591
- {
1592
- id: "tasting",
1593
- checked: form.values.tasting,
1594
- onCheckedChange: (checked) => form.setFieldValue("tasting", checked === true)
1595
- }
1596
- ),
1597
- /* @__PURE__ */ jsxRuntime.jsx(
1598
- Label,
1599
- {
1600
- htmlFor: "tasting",
1601
- className: "cursor-pointer font-normal",
1602
- children: "I'm interested in scheduling a tasting"
1603
- }
1604
- )
1605
- ] })
1606
- ] }),
1612
+ /* @__PURE__ */ jsxRuntime.jsx("div", { className: "grid grid-cols-12 gap-6", children: fields.map((field) => /* @__PURE__ */ jsxRuntime.jsx(
1613
+ "div",
1614
+ {
1615
+ className: getColumnSpanClass(field.columnSpan),
1616
+ children: /* @__PURE__ */ jsxRuntime.jsx(DynamicFormField, { field })
1617
+ },
1618
+ field.name
1619
+ )) }),
1607
1620
  actionsSlot || actions && actions.length > 0 ? actionsContent : /* @__PURE__ */ jsxRuntime.jsxs(
1608
1621
  Pressable,
1609
1622
  {
@@ -1613,14 +1626,7 @@ function ContactCatering({
1613
1626
  asButton: true,
1614
1627
  disabled: form.isSubmitting,
1615
1628
  children: [
1616
- buttonIcon ?? /* @__PURE__ */ jsxRuntime.jsx(
1617
- DynamicIcon,
1618
- {
1619
- name: "lucide/utensils",
1620
- size: 16,
1621
- className: "mr-2"
1622
- }
1623
- ),
1629
+ buttonIcon,
1624
1630
  buttonText
1625
1631
  ]
1626
1632
  }