Bob AI: Implement a 3D card flip animation on testimonial cards. Fro
This commit is contained in:
@@ -8,6 +8,82 @@ import { Star } from "lucide-react";
|
|||||||
import type { LucideIcon } from "lucide-react";
|
import type { LucideIcon } from "lucide-react";
|
||||||
import type { ButtonConfig, ButtonAnimationType, CardAnimationTypeWith3D, GridVariant, TitleSegment, TextboxLayout, InvertedBackground } from "@/components/cardStack/types";
|
import type { ButtonConfig, ButtonAnimationType, CardAnimationTypeWith3D, GridVariant, TitleSegment, TextboxLayout, InvertedBackground } from "@/components/cardStack/types";
|
||||||
|
|
||||||
|
const TestimonialCardFlip = memo(({ testimonial, className }: { testimonial: Testimonial; className?: string }) => {
|
||||||
|
const [isFlipped, setIsFlipped] = useState(false);
|
||||||
|
|
||||||
|
return (
|
||||||
|
<div
|
||||||
|
className={cls(
|
||||||
|
"h-full cursor-pointer perspective",
|
||||||
|
className
|
||||||
|
)}
|
||||||
|
onClick={() => setIsFlipped(!isFlipped)}
|
||||||
|
onMouseEnter={() => setIsFlipped(true)}
|
||||||
|
onMouseLeave={() => setIsFlipped(false)}
|
||||||
|
style={{
|
||||||
|
perspective: "1000px",
|
||||||
|
}}
|
||||||
|
>
|
||||||
|
<div
|
||||||
|
style={{
|
||||||
|
transformStyle: "preserve-3d",
|
||||||
|
transform: isFlipped ? "rotateY(180deg)" : "rotateY(0deg)",
|
||||||
|
transition: "transform 0.6s ease-in-out",
|
||||||
|
width: "100%",
|
||||||
|
height: "100%",
|
||||||
|
}}
|
||||||
|
>
|
||||||
|
{/* Front side - Name and Photo */}
|
||||||
|
<div
|
||||||
|
style={{
|
||||||
|
backfaceVisibility: "hidden",
|
||||||
|
WebkitBackfaceVisibility: "hidden",
|
||||||
|
}}
|
||||||
|
className="w-full h-full flex flex-col items-center justify-center p-6 bg-gradient-to-br from-slate-50 to-slate-100 dark:from-slate-900 dark:to-slate-800 rounded-lg border border-slate-200 dark:border-slate-700"
|
||||||
|
>
|
||||||
|
{testimonial.imageSrc && (
|
||||||
|
<img
|
||||||
|
src={testimonial.imageSrc}
|
||||||
|
alt={testimonial.imageAlt || testimonial.name}
|
||||||
|
className="w-24 h-24 rounded-full object-cover mb-4 border-4 border-white dark:border-slate-700 shadow-lg"
|
||||||
|
/>
|
||||||
|
)}
|
||||||
|
<h3 className="text-lg font-semibold text-slate-900 dark:text-white text-center">
|
||||||
|
{testimonial.name}
|
||||||
|
</h3>
|
||||||
|
<p className="text-sm text-slate-600 dark:text-slate-400 text-center mt-1">
|
||||||
|
{testimonial.role}
|
||||||
|
</p>
|
||||||
|
<p className="text-xs text-slate-500 dark:text-slate-500 text-center mt-1">
|
||||||
|
{testimonial.company}
|
||||||
|
</p>
|
||||||
|
<div className="flex gap-1 mt-3">
|
||||||
|
{Array.from({ length: testimonial.rating }).map((_, i) => (
|
||||||
|
<Star key={i} size={16} className="fill-yellow-400 text-yellow-400" />
|
||||||
|
))}
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
{/* Back side - Testimonial Text */}
|
||||||
|
<div
|
||||||
|
style={{
|
||||||
|
backfaceVisibility: "hidden",
|
||||||
|
WebkitBackfaceVisibility: "hidden",
|
||||||
|
transform: "rotateY(180deg)",
|
||||||
|
}}
|
||||||
|
className="absolute inset-0 w-full h-full flex flex-col items-center justify-center p-6 bg-gradient-to-br from-blue-50 to-indigo-50 dark:from-blue-900 dark:to-indigo-900 rounded-lg border border-blue-200 dark:border-indigo-700 shadow-lg"
|
||||||
|
>
|
||||||
|
<p className="text-sm text-slate-700 dark:text-slate-200 text-center leading-relaxed italic">
|
||||||
|
"{testimonial.testimonialText || "No testimonial text provided"}"
|
||||||
|
</p>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
);
|
||||||
|
});
|
||||||
|
|
||||||
|
TestimonialCardFlip.displayName = "TestimonialCardFlip";
|
||||||
|
|
||||||
type Testimonial = {
|
type Testimonial = {
|
||||||
id: string;
|
id: string;
|
||||||
name: string;
|
name: string;
|
||||||
|
|||||||
Reference in New Issue
Block a user