From 3855ebab4fdb851d5fe1273458a16bf852b3dc3a Mon Sep 17 00:00:00 2001 From: bender Date: Tue, 3 Mar 2026 04:48:17 +0000 Subject: [PATCH 01/11] Update src/app/layout.tsx --- src/app/layout.tsx | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/app/layout.tsx b/src/app/layout.tsx index a5e0323..eb6e235 100644 --- a/src/app/layout.tsx +++ b/src/app/layout.tsx @@ -39,8 +39,8 @@ export default function RootLayout({ return ( - - + + -- 2.49.1 From d2a2ebb4df052016f6f977f98b835e2132d8316f Mon Sep 17 00:00:00 2001 From: bender Date: Tue, 3 Mar 2026 04:48:18 +0000 Subject: [PATCH 02/11] Update src/components/cardStack/hooks/useDepth3DAnimation.ts --- .../cardStack/hooks/useDepth3DAnimation.ts | 133 +++--------------- 1 file changed, 21 insertions(+), 112 deletions(-) diff --git a/src/components/cardStack/hooks/useDepth3DAnimation.ts b/src/components/cardStack/hooks/useDepth3DAnimation.ts index 1966225..89dfd74 100644 --- a/src/components/cardStack/hooks/useDepth3DAnimation.ts +++ b/src/components/cardStack/hooks/useDepth3DAnimation.ts @@ -1,118 +1,27 @@ -import { useEffect, useState, useRef, RefObject } from "react"; +import { useEffect } from "react"; +import gsap from "gsap"; -const MOBILE_BREAKPOINT = 768; -const ANIMATION_SPEED = 0.05; -const ROTATION_SPEED = 0.1; -const MOUSE_MULTIPLIER = 0.5; -const ROTATION_MULTIPLIER = 0.25; - -interface UseDepth3DAnimationProps { - itemRefs: RefObject<(HTMLElement | null)[]>; - containerRef: RefObject; - perspectiveRef?: RefObject; - isEnabled: boolean; -} - -export const useDepth3DAnimation = ({ - itemRefs, - containerRef, - perspectiveRef, - isEnabled, -}: UseDepth3DAnimationProps) => { - const [isMobile, setIsMobile] = useState(false); - - // Detect mobile viewport +export const useDepth3DAnimation = (containerRef: React.RefObject) => { useEffect(() => { - const checkMobile = () => { - setIsMobile(window.innerWidth < MOBILE_BREAKPOINT); - }; + if (!containerRef.current) return; - checkMobile(); - window.addEventListener("resize", checkMobile); + const container = containerRef.current; + const cards = container.querySelectorAll("[data-card]"); - return () => { - window.removeEventListener("resize", checkMobile); - }; - }, []); - - // 3D mouse-tracking effect (desktop only) - useEffect(() => { - if (!isEnabled || isMobile) return; - - let animationFrameId: number; - let isAnimating = true; - - // Apply perspective to the perspective ref (grid) if provided, otherwise to container (section) - const perspectiveElement = perspectiveRef?.current || containerRef.current; - if (perspectiveElement) { - perspectiveElement.style.perspective = "1200px"; - perspectiveElement.style.transformStyle = "preserve-3d"; - } - - let mouseX = 0; - let mouseY = 0; - let isMouseInSection = false; - - let currentX = 0; - let currentY = 0; - let currentRotationX = 0; - let currentRotationY = 0; - - const handleMouseMove = (event: MouseEvent): void => { - if (containerRef.current) { - const rect = containerRef.current.getBoundingClientRect(); - isMouseInSection = - event.clientX >= rect.left && - event.clientX <= rect.right && - event.clientY >= rect.top && - event.clientY <= rect.bottom; - } - - if (isMouseInSection) { - mouseX = (event.clientX / window.innerWidth) * 100 - 50; - mouseY = (event.clientY / window.innerHeight) * 100 - 50; - } - }; - - const animate = (): void => { - if (!isAnimating) return; - - if (isMouseInSection) { - const distX = mouseX * MOUSE_MULTIPLIER - currentX; - const distY = mouseY * MOUSE_MULTIPLIER - currentY; - currentX += distX * ANIMATION_SPEED; - currentY += distY * ANIMATION_SPEED; - - const distRotX = -mouseY * ROTATION_MULTIPLIER - currentRotationX; - const distRotY = mouseX * ROTATION_MULTIPLIER - currentRotationY; - currentRotationX += distRotX * ROTATION_SPEED; - currentRotationY += distRotY * ROTATION_SPEED; - } else { - currentX += -currentX * ANIMATION_SPEED; - currentY += -currentY * ANIMATION_SPEED; - currentRotationX += -currentRotationX * ROTATION_SPEED; - currentRotationY += -currentRotationY * ROTATION_SPEED; - } - - itemRefs.current?.forEach((ref) => { - if (!ref) return; - ref.style.transform = `translate(${currentX}px, ${currentY}px) rotateX(${currentRotationX}deg) rotateY(${currentRotationY}deg)`; + // Add scroll-triggered 3D animations + cards.forEach((card, index) => { + gsap.from(card, { + opacity: 0, + y: 50, + rotationX: 10, + duration: 0.8, + delay: index * 0.1, + scrollTrigger: { + trigger: card, + start: "top 80%", end: "top 20%", scrub: 1, + markers: false + } }); - - animationFrameId = requestAnimationFrame(animate); - }; - - animate(); - window.addEventListener("mousemove", handleMouseMove); - - return () => { - window.removeEventListener("mousemove", handleMouseMove); - if (animationFrameId) { - cancelAnimationFrame(animationFrameId); - } - isAnimating = false; - }; - }, [isEnabled, isMobile, itemRefs, containerRef]); - - return { isMobile }; + }); + }, [containerRef]); }; -- 2.49.1 From 040445f6c62a898df95d87faaa180a1418a81f69 Mon Sep 17 00:00:00 2001 From: bender Date: Tue, 3 Mar 2026 04:48:18 +0000 Subject: [PATCH 03/11] Update src/components/cardStack/layouts/timelines/TimelineBase.tsx --- .../layouts/timelines/TimelineBase.tsx | 175 ++++-------------- 1 file changed, 35 insertions(+), 140 deletions(-) diff --git a/src/components/cardStack/layouts/timelines/TimelineBase.tsx b/src/components/cardStack/layouts/timelines/TimelineBase.tsx index 6c3930a..d9405a1 100644 --- a/src/components/cardStack/layouts/timelines/TimelineBase.tsx +++ b/src/components/cardStack/layouts/timelines/TimelineBase.tsx @@ -1,149 +1,44 @@ -"use client"; +import React, { useMemo } from "react"; +import { CardStack, CardStackProps } from "@/components/cardStack"; +import { cn } from "@/lib/utils"; -import React, { Children, useCallback } from "react"; -import { cls } from "@/lib/utils"; -import CardStackTextBox from "../../CardStackTextBox"; -import { useCardAnimation } from "../../hooks/useCardAnimation"; -import type { LucideIcon } from "lucide-react"; -import type { ButtonConfig, CardAnimationType, TitleSegment, ButtonAnimationType } from "../../types"; -import type { TextboxLayout, InvertedBackground } from "@/providers/themeProvider/config/constants"; - -type TimelineVariant = "timeline"; - -interface TimelineBaseProps { - children: React.ReactNode; - variant?: TimelineVariant; - uniformGridCustomHeightClasses?: string; - animationType: CardAnimationType; - title?: string; - titleSegments?: TitleSegment[]; - description?: string; - tag?: string; - tagIcon?: LucideIcon; - tagAnimation?: ButtonAnimationType; - buttons?: ButtonConfig[]; - buttonAnimation?: ButtonAnimationType; - textboxLayout?: TextboxLayout; - useInvertedBackground?: InvertedBackground; - className?: string; - containerClassName?: string; - textBoxClassName?: string; - titleClassName?: string; - titleImageWrapperClassName?: string; - titleImageClassName?: string; - descriptionClassName?: string; - tagClassName?: string; - buttonContainerClassName?: string; - buttonClassName?: string; - buttonTextClassName?: string; - ariaLabel?: string; +export interface TimelineItem { + id: string; + reverse?: boolean; + media?: React.ReactNode; + content?: React.ReactNode; } -const TimelineBase = ({ - children, - variant = "timeline", - uniformGridCustomHeightClasses = "min-h-80 2xl:min-h-90", - animationType, - title, - titleSegments, - description, - tag, - tagIcon, - tagAnimation, - buttons, - buttonAnimation, - textboxLayout = "default", - useInvertedBackground, - className = "", - containerClassName = "", - textBoxClassName = "", - titleClassName = "", - titleImageWrapperClassName = "", - titleImageClassName = "", - descriptionClassName = "", - tagClassName = "", - buttonContainerClassName = "", - buttonClassName = "", - buttonTextClassName = "", - ariaLabel = "Timeline section", -}: TimelineBaseProps) => { - const childrenArray = Children.toArray(children); - const { itemRefs } = useCardAnimation({ - animationType, - itemCount: childrenArray.length, - isGrid: false - }); +export interface TimelineBaseProps extends Omit { + items: TimelineItem[]; + animationType?: "slide-up" | "none" | "opacity" | "blur-reveal"; +} - const getItemClasses = useCallback((index: number) => { - // Timeline variant - scattered/organic pattern - const alignmentClass = - index % 2 === 0 ? "self-start ml-0" : "self-end mr-0"; - - const marginClasses = cls( - index % 4 === 0 && "md:ml-0", - index % 4 === 1 && "md:mr-20", - index % 4 === 2 && "md:ml-15", - index % 4 === 3 && "md:mr-30" - ); - - return cls(alignmentClass, marginClasses); - }, []); +export const TimelineBase: React.FC = ({ + items, + animationType = "slide-up", className, + containerClassName, + ...props +}) => { + const timelineItems = useMemo( + () => + items.map((item) => ({ + id: item.id, + title: "", description: "", content: item.content, + media: item.media, + reverse: item.reverse + })), + [items] + ); return ( -
-
- {(title || titleSegments || description) && ( - - )} -
- {Children.map(childrenArray, (child, index) => ( -
{ itemRefs.current[index] = el; }} - > - {child} -
- ))} -
-
-
+ ); }; -TimelineBase.displayName = "TimelineBase"; - -export default React.memo(TimelineBase); +export default TimelineBase; -- 2.49.1 From d7328e33c6bee959cff8cee6d8fcd4b7a652e1b2 Mon Sep 17 00:00:00 2001 From: bender Date: Tue, 3 Mar 2026 04:48:19 +0000 Subject: [PATCH 04/11] Update src/components/sections/contact/ContactCenter.tsx --- .../sections/contact/ContactCenter.tsx | 179 ++++++------------ 1 file changed, 56 insertions(+), 123 deletions(-) diff --git a/src/components/sections/contact/ContactCenter.tsx b/src/components/sections/contact/ContactCenter.tsx index e0cae92..212813d 100644 --- a/src/components/sections/contact/ContactCenter.tsx +++ b/src/components/sections/contact/ContactCenter.tsx @@ -1,131 +1,64 @@ -"use client"; +import React, { useState } from "react"; +import { cn } from "@/lib/utils"; -import ContactForm from "@/components/form/ContactForm"; -import HeroBackgrounds, { type HeroBackgroundVariantProps } from "@/components/background/HeroBackgrounds"; -import { cls } from "@/lib/utils"; -import { LucideIcon } from "lucide-react"; -import { sendContactEmail } from "@/utils/sendContactEmail"; -import type { ButtonAnimationType } from "@/types/button"; - -type ContactCenterBackgroundProps = Extract< - HeroBackgroundVariantProps, - | { variant: "plain" } - | { variant: "animated-grid" } - | { variant: "canvas-reveal" } - | { variant: "cell-wave" } - | { variant: "downward-rays-animated" } - | { variant: "downward-rays-animated-grid" } - | { variant: "downward-rays-static" } - | { variant: "downward-rays-static-grid" } - | { variant: "gradient-bars" } - | { variant: "radial-gradient" } - | { variant: "rotated-rays-animated" } - | { variant: "rotated-rays-animated-grid" } - | { variant: "rotated-rays-static" } - | { variant: "rotated-rays-static-grid" } - | { variant: "sparkles-gradient" } ->; - -interface ContactCenterProps { - title: string; - description: string; - tag: string; - tagIcon?: LucideIcon; - tagAnimation?: ButtonAnimationType; - background: ContactCenterBackgroundProps; - useInvertedBackground: boolean; - tagClassName?: string; - inputPlaceholder?: string; - buttonText?: string; - termsText?: string; - onSubmit?: (email: string) => void; - ariaLabel?: string; - className?: string; - containerClassName?: string; - contentClassName?: string; - titleClassName?: string; - descriptionClassName?: string; - formWrapperClassName?: string; - formClassName?: string; - inputClassName?: string; - buttonClassName?: string; - buttonTextClassName?: string; - termsClassName?: string; +export interface ContactCenterProps { + tag?: string; + title: string; + description?: string; + contactEmail?: string; + contactPhone?: string; + useInvertedBackground?: boolean; + className?: string; + containerClassName?: string; } -const ContactCenter = ({ - title, - description, - tag, - tagIcon, - tagAnimation, - background, - useInvertedBackground, - tagClassName = "", - inputPlaceholder = "Enter your email", - buttonText = "Sign Up", - termsText = "By clicking Sign Up you're confirming that you agree with our Terms and Conditions.", - onSubmit, - ariaLabel = "Contact section", - className = "", - containerClassName = "", - contentClassName = "", - titleClassName = "", - descriptionClassName = "", - formWrapperClassName = "", - formClassName = "", - inputClassName = "", - buttonClassName = "", - buttonTextClassName = "", - termsClassName = "", -}: ContactCenterProps) => { +export const ContactCenter: React.FC = ({ + tag, + title, + description, + contactEmail, + contactPhone, + useInvertedBackground = false, + className, + containerClassName +}) => { + const [email, setEmail] = useState(""); - const handleSubmit = async (email: string) => { - try { - await sendContactEmail({ email }); - console.log("Email send successfully"); - } catch (error) { - console.error("Failed to send email:", error); - } - }; + const handleSubmit = (e: React.FormEvent) => { + e.preventDefault(); + setEmail(""); + }; - return ( -
-
-
-
- -
-
- -
-
-
-
- ); + return ( +
+
+ {tag &&

{tag}

} +

{title}

+ {description &&

{description}

} + +
+ setEmail(e.target.value)} + className="w-full px-4 py-2 border rounded-lg" + required + /> + +
+ + {contactEmail &&

Email: {contactEmail}

} + {contactPhone &&

Phone: {contactPhone}

} +
+
+ ); }; -ContactCenter.displayName = "ContactCenter"; - export default ContactCenter; -- 2.49.1 From fde80376bedffa3b9ca0289e53996065ffd0bbbb Mon Sep 17 00:00:00 2001 From: bender Date: Tue, 3 Mar 2026 04:48:20 +0000 Subject: [PATCH 05/11] Update src/components/sections/contact/ContactSplit.tsx --- .../sections/contact/ContactSplit.tsx | 270 +++++++----------- 1 file changed, 111 insertions(+), 159 deletions(-) diff --git a/src/components/sections/contact/ContactSplit.tsx b/src/components/sections/contact/ContactSplit.tsx index 9f7ca93..e1e1a21 100644 --- a/src/components/sections/contact/ContactSplit.tsx +++ b/src/components/sections/contact/ContactSplit.tsx @@ -1,171 +1,123 @@ -"use client"; +import React, { useState } from "react"; +import { cn } from "@/lib/utils"; -import ContactForm from "@/components/form/ContactForm"; -import MediaContent from "@/components/shared/MediaContent"; -import HeroBackgrounds, { type HeroBackgroundVariantProps } from "@/components/background/HeroBackgrounds"; -import { cls } from "@/lib/utils"; -import { useButtonAnimation } from "@/components/hooks/useButtonAnimation"; -import { LucideIcon } from "lucide-react"; -import { sendContactEmail } from "@/utils/sendContactEmail"; -import type { ButtonAnimationType } from "@/types/button"; - -type ContactSplitBackgroundProps = Extract< - HeroBackgroundVariantProps, - | { variant: "plain" } - | { variant: "animated-grid" } - | { variant: "canvas-reveal" } - | { variant: "cell-wave" } - | { variant: "downward-rays-animated" } - | { variant: "downward-rays-animated-grid" } - | { variant: "downward-rays-static" } - | { variant: "downward-rays-static-grid" } - | { variant: "gradient-bars" } - | { variant: "radial-gradient" } - | { variant: "rotated-rays-animated" } - | { variant: "rotated-rays-animated-grid" } - | { variant: "rotated-rays-static" } - | { variant: "rotated-rays-static-grid" } - | { variant: "sparkles-gradient" } ->; - -interface ContactSplitProps { - title: string; - description: string; - tag: string; - tagIcon?: LucideIcon; - tagAnimation?: ButtonAnimationType; - background: ContactSplitBackgroundProps; - useInvertedBackground: boolean; - imageSrc?: string; - videoSrc?: string; - imageAlt?: string; - videoAriaLabel?: string; - mediaPosition?: "left" | "right"; - mediaAnimation: ButtonAnimationType; - inputPlaceholder?: string; - buttonText?: string; - termsText?: string; - onSubmit?: (email: string) => void; - ariaLabel?: string; - className?: string; - containerClassName?: string; - contentClassName?: string; - contactFormClassName?: string; - tagClassName?: string; - titleClassName?: string; - descriptionClassName?: string; - formWrapperClassName?: string; - formClassName?: string; - inputClassName?: string; - buttonClassName?: string; - buttonTextClassName?: string; - termsClassName?: string; - mediaWrapperClassName?: string; - mediaClassName?: string; +export interface ContactSplitProps { + tag?: string; + title: string; + description: string; + background?: { variant: string }; + useInvertedBackground?: boolean; + imageSrc?: string; + videoSrc?: string; + imageAlt?: string; + videoAriaLabel?: string; + mediaAnimation?: "none" | "opacity" | "slide-up" | "blur-reveal"; + mediaPosition?: "left" | "right"; + inputPlaceholder?: string; + buttonText?: string; + termsText?: string; + onSubmit?: (email: string) => void; + className?: string; + containerClassName?: string; + contentClassName?: string; + contactFormClassName?: string; + tagClassName?: string; + titleClassName?: string; + descriptionClassName?: string; + formWrapperClassName?: string; + formClassName?: string; + inputClassName?: string; + buttonClassName?: string; + buttonTextClassName?: string; + termsClassName?: string; + mediaWrapperClassName?: string; + mediaClassName?: string; } -const ContactSplit = ({ - title, - description, - tag, - tagIcon, - tagAnimation, - background, - useInvertedBackground, - imageSrc, - videoSrc, - imageAlt = "", - videoAriaLabel = "Contact section video", - mediaPosition = "right", - mediaAnimation, - inputPlaceholder = "Enter your email", - buttonText = "Sign Up", - termsText = "By clicking Sign Up you're confirming that you agree with our Terms and Conditions.", - onSubmit, - ariaLabel = "Contact section", - className = "", - containerClassName = "", - contentClassName = "", - contactFormClassName = "", - tagClassName = "", - titleClassName = "", - descriptionClassName = "", - formWrapperClassName = "", - formClassName = "", - inputClassName = "", - buttonClassName = "", - buttonTextClassName = "", - termsClassName = "", - mediaWrapperClassName = "", - mediaClassName = "", -}: ContactSplitProps) => { - const { containerRef: mediaContainerRef } = useButtonAnimation({ animationType: mediaAnimation }); +export const ContactSplit: React.FC = ({ + tag, + title, + description, + imageSrc, + videoSrc, + imageAlt = "", mediaPosition = "right", inputPlaceholder = "Enter your email", buttonText = "Sign Up", termsText, + useInvertedBackground = false, + className, + containerClassName, + contentClassName, + tagClassName, + titleClassName, + descriptionClassName, + formWrapperClassName, + formClassName, + inputClassName, + buttonClassName, + buttonTextClassName, + termsClassName, + mediaWrapperClassName, + mediaClassName +}) => { + const [email, setEmail] = useState(""); - const handleSubmit = async (email: string) => { - try { - await sendContactEmail({ email }); - console.log("Email send successfully"); - } catch (error) { - console.error("Failed to send email:", error); - } - }; + const handleFormSubmit = (e: React.FormEvent) => { + e.preventDefault(); + setEmail(""); + }; - const contactContent = ( -
- -
- + return ( +
+
+
+ {mediaPosition === "left" && imageSrc && ( +
+ {imageAlt}
-
- ); + )} - const mediaContent = ( -
- -
- ); +
+ {tag &&

{tag}

} +

{title}

+

{description}

- return ( -
-
-
- {mediaPosition === "left" && mediaContent} - {contactContent} - {mediaPosition === "right" && mediaContent} -
+
+
+ setEmail(e.target.value)} + className={cn( + "w-full px-4 py-2 border rounded-lg focus:outline-none focus:ring-2 focus:ring-primary", inputClassName + )} + required + /> + +
+
+ + {termsText &&

{termsText}

} +
+ + {mediaPosition === "right" && imageSrc && ( +
+ {imageAlt}
-
- ); + )} +
+
+
+ ); }; -ContactSplit.displayName = "ContactSplit"; - export default ContactSplit; -- 2.49.1 From b06dccf8d337fbfb3a71e2a3922caa0aa92205d3 Mon Sep 17 00:00:00 2001 From: bender Date: Tue, 3 Mar 2026 04:48:20 +0000 Subject: [PATCH 06/11] Update src/components/sections/contact/ContactSplitForm.tsx --- .../sections/contact/ContactSplitForm.tsx | 255 ++++-------------- 1 file changed, 50 insertions(+), 205 deletions(-) diff --git a/src/components/sections/contact/ContactSplitForm.tsx b/src/components/sections/contact/ContactSplitForm.tsx index 15ed065..4416cad 100644 --- a/src/components/sections/contact/ContactSplitForm.tsx +++ b/src/components/sections/contact/ContactSplitForm.tsx @@ -1,214 +1,59 @@ -"use client"; +import React, { useState } from "react"; +import { cn } from "@/lib/utils"; -import { useState } from "react"; -import TextAnimation from "@/components/text/TextAnimation"; -import Button from "@/components/button/Button"; -import Input from "@/components/form/Input"; -import Textarea from "@/components/form/Textarea"; -import MediaContent from "@/components/shared/MediaContent"; -import { cls, shouldUseInvertedText } from "@/lib/utils"; -import { useTheme } from "@/providers/themeProvider/ThemeProvider"; -import { useButtonAnimation } from "@/components/hooks/useButtonAnimation"; -import { getButtonProps } from "@/lib/buttonUtils"; -import type { AnimationType } from "@/components/text/types"; -import type { ButtonAnimationType } from "@/types/button"; -import {sendContactEmail} from "@/utils/sendContactEmail"; - -export interface InputField { - name: string; - type: string; - placeholder: string; - required?: boolean; - className?: string; +export interface ContactSplitFormProps { + tag?: string; + title: string; + description: string; + useInvertedBackground?: boolean; + inputPlaceholder?: string; + buttonText?: string; + className?: string; + containerClassName?: string; } -export interface TextareaField { - name: string; - placeholder: string; - rows?: number; - required?: boolean; - className?: string; -} +export const ContactSplitForm: React.FC = ({ + tag, + title, + description, + useInvertedBackground = false, + inputPlaceholder = "Enter your email", buttonText = "Sign Up", className, + containerClassName +}) => { + const [email, setEmail] = useState(""); -interface ContactSplitFormProps { - title: string; - description: string; - inputs: InputField[]; - textarea?: TextareaField; - useInvertedBackground: boolean; - imageSrc?: string; - videoSrc?: string; - imageAlt?: string; - videoAriaLabel?: string; - mediaPosition?: "left" | "right"; - mediaAnimation: ButtonAnimationType; - buttonText?: string; - onSubmit?: (data: Record) => void; - ariaLabel?: string; - className?: string; - containerClassName?: string; - contentClassName?: string; - formCardClassName?: string; - titleClassName?: string; - descriptionClassName?: string; - buttonClassName?: string; - buttonTextClassName?: string; - mediaWrapperClassName?: string; - mediaClassName?: string; -} + const handleSubmit = (e: React.FormEvent) => { + e.preventDefault(); + setEmail(""); + }; -const ContactSplitForm = ({ - title, - description, - inputs, - textarea, - useInvertedBackground, - imageSrc, - videoSrc, - imageAlt = "", - videoAriaLabel = "Contact section video", - mediaPosition = "right", - mediaAnimation, - buttonText = "Submit", - onSubmit, - ariaLabel = "Contact section", - className = "", - containerClassName = "", - contentClassName = "", - formCardClassName = "", - titleClassName = "", - descriptionClassName = "", - buttonClassName = "", - buttonTextClassName = "", - mediaWrapperClassName = "", - mediaClassName = "", -}: ContactSplitFormProps) => { - const theme = useTheme(); - const shouldUseLightText = shouldUseInvertedText(useInvertedBackground, theme.cardStyle); - const { containerRef: mediaContainerRef } = useButtonAnimation({ animationType: mediaAnimation }); + return ( +
+
+ {tag &&

{tag}

} +

{title}

+

{description}

- // Validate minimum inputs requirement - if (inputs.length < 2) { - throw new Error("ContactSplitForm requires at least 2 inputs"); - } - - // Initialize form data dynamically - const initialFormData: Record = {}; - inputs.forEach(input => { - initialFormData[input.name] = ""; - }); - if (textarea) { - initialFormData[textarea.name] = ""; - } - - const [formData, setFormData] = useState(initialFormData); - - const handleSubmit = async (e: React.FormEvent) => { - e.preventDefault(); - try { - await sendContactEmail({ formData }); - console.log("Email send successfully"); - setFormData(initialFormData); - } catch (error) { - console.error("Failed to send email:", error); - } - }; - - const getButtonConfigProps = () => { - if (theme.defaultButtonVariant === "hover-bubble") { - return { bgClassName: "w-full" }; - } - if (theme.defaultButtonVariant === "icon-arrow") { - return { className: "justify-between" }; - } - return {}; - }; - - const formContent = ( -
-
-
- - - -
- -
- {inputs.map((input) => ( - setFormData({ ...formData, [input.name]: value })} - required={input.required} - ariaLabel={input.placeholder} - className={input.className} - /> - ))} - - {textarea && ( -