Bob AI: Add a 3D card flip animation to testimonial cards. On the fr
This commit is contained in:
@@ -1,6 +1,6 @@
|
|||||||
"use client";
|
"use client";
|
||||||
|
|
||||||
import { memo } from "react";
|
import { memo, useState } from "react";
|
||||||
import CardStack from "@/components/cardStack/CardStack";
|
import CardStack from "@/components/cardStack/CardStack";
|
||||||
import MediaContent from "@/components/shared/MediaContent";
|
import MediaContent from "@/components/shared/MediaContent";
|
||||||
import { cls } from "@/lib/utils";
|
import { cls } from "@/lib/utils";
|
||||||
@@ -18,6 +18,7 @@ type Testimonial = {
|
|||||||
videoSrc?: string;
|
videoSrc?: string;
|
||||||
imageAlt?: string;
|
imageAlt?: string;
|
||||||
videoAriaLabel?: string;
|
videoAriaLabel?: string;
|
||||||
|
testimonialText?: string;
|
||||||
};
|
};
|
||||||
|
|
||||||
interface TestimonialCardOneProps {
|
interface TestimonialCardOneProps {
|
||||||
@@ -50,6 +51,7 @@ interface TestimonialCardOneProps {
|
|||||||
nameClassName?: string;
|
nameClassName?: string;
|
||||||
roleClassName?: string;
|
roleClassName?: string;
|
||||||
companyClassName?: string;
|
companyClassName?: string;
|
||||||
|
testimonialTextClassName?: string;
|
||||||
gridClassName?: string;
|
gridClassName?: string;
|
||||||
carouselClassName?: string;
|
carouselClassName?: string;
|
||||||
controlsClassName?: string;
|
controlsClassName?: string;
|
||||||
@@ -69,6 +71,7 @@ interface TestimonialCardProps {
|
|||||||
nameClassName?: string;
|
nameClassName?: string;
|
||||||
roleClassName?: string;
|
roleClassName?: string;
|
||||||
companyClassName?: string;
|
companyClassName?: string;
|
||||||
|
testimonialTextClassName?: string;
|
||||||
}
|
}
|
||||||
|
|
||||||
const TestimonialCard = memo(({
|
const TestimonialCard = memo(({
|
||||||
@@ -80,9 +83,37 @@ const TestimonialCard = memo(({
|
|||||||
nameClassName = "",
|
nameClassName = "",
|
||||||
roleClassName = "",
|
roleClassName = "",
|
||||||
companyClassName = "",
|
companyClassName = "",
|
||||||
|
testimonialTextClassName = "",
|
||||||
}: TestimonialCardProps) => {
|
}: TestimonialCardProps) => {
|
||||||
|
const [isFlipped, setIsFlipped] = useState(false);
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<div className={cls("relative h-full rounded-theme-capped overflow-hidden group", cardClassName)}>
|
<div
|
||||||
|
className={cls("relative h-full rounded-theme-capped overflow-hidden group cursor-pointer", cardClassName)}
|
||||||
|
style={{
|
||||||
|
perspective: "1000px",
|
||||||
|
}}
|
||||||
|
onMouseEnter={() => setIsFlipped(true)}
|
||||||
|
onMouseLeave={() => setIsFlipped(false)}
|
||||||
|
onClick={() => setIsFlipped(!isFlipped)}
|
||||||
|
>
|
||||||
|
<div
|
||||||
|
style={{
|
||||||
|
transformStyle: "preserve-3d",
|
||||||
|
transform: isFlipped ? "rotateY(180deg)" : "rotateY(0deg)",
|
||||||
|
transition: "transform 0.6s cubic-bezier(0.68, -0.55, 0.265, 1.55)",
|
||||||
|
width: "100%",
|
||||||
|
height: "100%",
|
||||||
|
}}
|
||||||
|
>
|
||||||
|
{/* Front of card */}
|
||||||
|
<div
|
||||||
|
style={{
|
||||||
|
backfaceVisibility: "hidden",
|
||||||
|
WebkitBackfaceVisibility: "hidden",
|
||||||
|
}}
|
||||||
|
className="w-full h-full"
|
||||||
|
>
|
||||||
<MediaContent
|
<MediaContent
|
||||||
imageSrc={testimonial.imageSrc}
|
imageSrc={testimonial.imageSrc}
|
||||||
videoSrc={testimonial.videoSrc}
|
videoSrc={testimonial.videoSrc}
|
||||||
@@ -119,6 +150,22 @@ const TestimonialCard = memo(({
|
|||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
|
{/* Back of card */}
|
||||||
|
<div
|
||||||
|
style={{
|
||||||
|
backfaceVisibility: "hidden",
|
||||||
|
WebkitBackfaceVisibility: "hidden",
|
||||||
|
transform: "rotateY(180deg)",
|
||||||
|
}}
|
||||||
|
className="absolute inset-0 w-full h-full card backdrop-blur-xs rounded-theme-capped p-6 flex flex-col justify-center items-center"
|
||||||
|
>
|
||||||
|
<p className={cls("text-base text-foreground leading-relaxed text-center", testimonialTextClassName)}>
|
||||||
|
{testimonial.testimonialText || "No testimonial text provided"}
|
||||||
|
</p>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
);
|
);
|
||||||
});
|
});
|
||||||
|
|
||||||
@@ -154,6 +201,7 @@ const TestimonialCardOne = ({
|
|||||||
nameClassName = "",
|
nameClassName = "",
|
||||||
roleClassName = "",
|
roleClassName = "",
|
||||||
companyClassName = "",
|
companyClassName = "",
|
||||||
|
testimonialTextClassName = "",
|
||||||
gridClassName = "",
|
gridClassName = "",
|
||||||
carouselClassName = "",
|
carouselClassName = "",
|
||||||
controlsClassName = "",
|
controlsClassName = "",
|
||||||
@@ -208,6 +256,7 @@ const TestimonialCardOne = ({
|
|||||||
nameClassName={nameClassName}
|
nameClassName={nameClassName}
|
||||||
roleClassName={roleClassName}
|
roleClassName={roleClassName}
|
||||||
companyClassName={companyClassName}
|
companyClassName={companyClassName}
|
||||||
|
testimonialTextClassName={testimonialTextClassName}
|
||||||
/>
|
/>
|
||||||
))}
|
))}
|
||||||
</CardStack>
|
</CardStack>
|
||||||
|
|||||||
Reference in New Issue
Block a user