Initial commit
This commit is contained in:
109
src/components/sections/testimonial/TestimonialCardFifteen.tsx
Normal file
109
src/components/sections/testimonial/TestimonialCardFifteen.tsx
Normal file
@@ -0,0 +1,109 @@
|
||||
"use client";
|
||||
|
||||
import { useState, useEffect } from "react";
|
||||
import { Star } from "lucide-react";
|
||||
import TextAnimation from "@/components/text/TextAnimation";
|
||||
import AvatarGroup from "@/components/shared/AvatarGroup";
|
||||
import type { Avatar } from "@/components/shared/AvatarGroup";
|
||||
import { cls } from "@/lib/utils";
|
||||
import { useTheme } from "@/providers/themeProvider/ThemeProvider";
|
||||
import type { InvertedBackground } from "@/providers/themeProvider/config/constants";
|
||||
|
||||
interface TestimonialCardFifteenProps {
|
||||
testimonial: string;
|
||||
rating: number;
|
||||
author: string;
|
||||
avatars: Avatar[];
|
||||
useInvertedBackground: InvertedBackground;
|
||||
ariaLabel?: string;
|
||||
className?: string;
|
||||
containerClassName?: string;
|
||||
ratingClassName?: string;
|
||||
starClassName?: string;
|
||||
testimonialClassName?: string;
|
||||
avatarGroupClassName?: string;
|
||||
avatarClassName?: string;
|
||||
avatarImageClassName?: string;
|
||||
}
|
||||
|
||||
const TestimonialCardFifteen = ({
|
||||
testimonial,
|
||||
rating,
|
||||
author,
|
||||
avatars,
|
||||
useInvertedBackground,
|
||||
ariaLabel = "Testimonial section",
|
||||
className = "",
|
||||
containerClassName = "",
|
||||
ratingClassName = "",
|
||||
starClassName = "",
|
||||
testimonialClassName = "",
|
||||
avatarGroupClassName = "",
|
||||
avatarClassName = "",
|
||||
avatarImageClassName = "",
|
||||
}: TestimonialCardFifteenProps) => {
|
||||
const theme = useTheme();
|
||||
const [isMobile, setIsMobile] = useState(false);
|
||||
|
||||
useEffect(() => {
|
||||
const checkMobile = () => setIsMobile(window.innerWidth < 768);
|
||||
checkMobile();
|
||||
window.addEventListener("resize", checkMobile);
|
||||
return () => window.removeEventListener("resize", checkMobile);
|
||||
}, []);
|
||||
|
||||
return (
|
||||
<section
|
||||
aria-label={ariaLabel}
|
||||
className={cls("relative py-20 w-full", useInvertedBackground === "invertDefault" && "bg-foreground", className)}
|
||||
>
|
||||
<div className={cls("w-content-width mx-auto flex flex-col items-center gap-6", containerClassName)}>
|
||||
<div className={cls("relative z-1 flex gap-1 -mb-1", ratingClassName)}>
|
||||
{Array.from({ length: 5 }).map((_, index) => (
|
||||
<Star
|
||||
key={index}
|
||||
className={cls(
|
||||
"h-6 w-auto text-accent",
|
||||
index < rating ? "fill-accent" : "fill-transparent",
|
||||
starClassName
|
||||
)}
|
||||
strokeWidth={1.5}
|
||||
/>
|
||||
))}
|
||||
</div>
|
||||
|
||||
<TextAnimation
|
||||
type={theme.defaultTextAnimation}
|
||||
text={testimonial}
|
||||
variant="words-trigger"
|
||||
as="p"
|
||||
className={cls(
|
||||
"text-3xl md:text-5xl font-medium text-balance text-center leading-tight",
|
||||
useInvertedBackground === "invertDefault" && "text-background",
|
||||
testimonialClassName
|
||||
)}
|
||||
/>
|
||||
|
||||
<p className={cls(
|
||||
"text-xl text-center mb-1",
|
||||
useInvertedBackground === "invertDefault" && "text-background"
|
||||
)}>
|
||||
{author}
|
||||
</p>
|
||||
|
||||
<AvatarGroup
|
||||
avatars={avatars}
|
||||
maxVisible={isMobile ? 3 : 6}
|
||||
className={cls("justify-center", avatarGroupClassName)}
|
||||
avatarClassName={avatarClassName}
|
||||
avatarImageClassName={cls("h-[var(--width-17_5)] md:h-[var(--width-5)]", avatarImageClassName)}
|
||||
avatarOverlapClassName="-ml-8"
|
||||
/>
|
||||
</div>
|
||||
</section>
|
||||
);
|
||||
};
|
||||
|
||||
TestimonialCardFifteen.displayName = "TestimonialCardFifteen";
|
||||
|
||||
export default TestimonialCardFifteen;
|
||||
230
src/components/sections/testimonial/TestimonialCardFive.tsx
Normal file
230
src/components/sections/testimonial/TestimonialCardFive.tsx
Normal file
@@ -0,0 +1,230 @@
|
||||
"use client";
|
||||
|
||||
import { memo } from "react";
|
||||
|
||||
|
||||
import FullWidthCarousel from "@/components/cardStack/layouts/carousels/FullWidthCarousel";
|
||||
import MediaContent from "@/components/shared/MediaContent";
|
||||
import Tag from "@/components/shared/Tag";
|
||||
import TestimonialAuthor from "@/components/shared/TestimonialAuthor";
|
||||
import { cls, shouldUseInvertedText } from "@/lib/utils";
|
||||
import { useTheme } from "@/providers/themeProvider/ThemeProvider";
|
||||
import type { LucideIcon } from "lucide-react";
|
||||
import type { ButtonConfig, TitleSegment, TextboxLayout, InvertedBackground } from "@/components/cardStack/types";
|
||||
|
||||
type Testimonial = {
|
||||
id: string;
|
||||
name: string;
|
||||
date: string;
|
||||
title: string;
|
||||
quote: string;
|
||||
tag: string;
|
||||
avatarSrc: string;
|
||||
avatarAlt?: string;
|
||||
imageSrc?: string;
|
||||
videoSrc?: string;
|
||||
imageAlt?: string;
|
||||
videoAriaLabel?: string;
|
||||
};
|
||||
|
||||
interface TestimonialCardFiveProps {
|
||||
testimonials: Testimonial[];
|
||||
title: string;
|
||||
titleSegments?: TitleSegment[];
|
||||
description: string;
|
||||
tag?: string;
|
||||
tagIcon?: LucideIcon;
|
||||
buttons?: ButtonConfig[];
|
||||
textboxLayout: TextboxLayout;
|
||||
useInvertedBackground: InvertedBackground;
|
||||
ariaLabel?: string;
|
||||
className?: string;
|
||||
containerClassName?: string;
|
||||
cardClassName?: string;
|
||||
textBoxTitleClassName?: string;
|
||||
textBoxTitleImageWrapperClassName?: string;
|
||||
textBoxTitleImageClassName?: string;
|
||||
textBoxDescriptionClassName?: string;
|
||||
cardTagClassName?: string;
|
||||
cardTitleClassName?: string;
|
||||
cardQuoteClassName?: string;
|
||||
cardAuthorClassName?: string;
|
||||
cardAvatarWrapperClassName?: string;
|
||||
cardAvatarClassName?: string;
|
||||
cardNameClassName?: string;
|
||||
cardDateClassName?: string;
|
||||
cardImageClassName?: string;
|
||||
carouselClassName?: string;
|
||||
dotsClassName?: string;
|
||||
textBoxClassName?: string;
|
||||
textBoxTagClassName?: string;
|
||||
textBoxButtonContainerClassName?: string;
|
||||
textBoxButtonClassName?: string;
|
||||
textBoxButtonTextClassName?: string;
|
||||
}
|
||||
|
||||
interface TestimonialCardProps {
|
||||
testimonial: Testimonial;
|
||||
useInvertedBackground: "noInvert" | "invertDefault";
|
||||
cardClassName?: string;
|
||||
tagClassName?: string;
|
||||
titleClassName?: string;
|
||||
quoteClassName?: string;
|
||||
authorClassName?: string;
|
||||
avatarWrapperClassName?: string;
|
||||
avatarClassName?: string;
|
||||
nameClassName?: string;
|
||||
dateClassName?: string;
|
||||
imageClassName?: string;
|
||||
}
|
||||
|
||||
const TestimonialCard = memo(({
|
||||
testimonial,
|
||||
useInvertedBackground,
|
||||
cardClassName = "",
|
||||
tagClassName = "",
|
||||
titleClassName = "",
|
||||
quoteClassName = "",
|
||||
authorClassName = "",
|
||||
avatarWrapperClassName = "",
|
||||
avatarClassName = "",
|
||||
nameClassName = "",
|
||||
dateClassName = "",
|
||||
imageClassName = "",
|
||||
}: TestimonialCardProps) => {
|
||||
const theme = useTheme();
|
||||
const shouldUseLightText = shouldUseInvertedText(useInvertedBackground, theme.cardStyle);
|
||||
|
||||
return (
|
||||
<div className={cls("card h-fit md:h-full rounded-theme-capped overflow-hidden flex flex-col-reverse md:grid md:grid-cols-2 gap-0", cardClassName)}>
|
||||
<div className="relative z-1 p-6 md:p-10 flex flex-col gap-10 justify-between">
|
||||
<div className="flex flex-col gap-4 md:gap-6">
|
||||
<Tag
|
||||
text={testimonial.tag}
|
||||
useInvertedBackground={useInvertedBackground}
|
||||
className={tagClassName}
|
||||
/>
|
||||
|
||||
<div className="flex flex-col gap-4 overflow-hidden">
|
||||
<h3 className={cls("text-4xl font-medium leading-tight line-clamp-3", shouldUseLightText ? "text-background" : "text-foreground", titleClassName)}>
|
||||
{testimonial.title}
|
||||
</h3>
|
||||
<blockquote className={cls("text-base md:text-lg leading-tight line-clamp-10", shouldUseLightText ? "text-background/75" : "text-foreground/75", quoteClassName)}>
|
||||
{testimonial.quote}
|
||||
</blockquote>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<TestimonialAuthor
|
||||
name={testimonial.name}
|
||||
subtitle={testimonial.date}
|
||||
imageSrc={testimonial.avatarSrc}
|
||||
imageAlt={testimonial.avatarAlt}
|
||||
useInvertedBackground={useInvertedBackground}
|
||||
className={authorClassName}
|
||||
imageWrapperClassName={avatarWrapperClassName}
|
||||
imageClassName={avatarClassName}
|
||||
nameClassName={nameClassName}
|
||||
subtitleClassName={dateClassName}
|
||||
/>
|
||||
</div>
|
||||
|
||||
<div className="relative z-1 min-h-0 aspect-square">
|
||||
<MediaContent
|
||||
imageSrc={testimonial.imageSrc}
|
||||
videoSrc={testimonial.videoSrc}
|
||||
imageAlt={testimonial.imageAlt}
|
||||
videoAriaLabel={testimonial.videoAriaLabel}
|
||||
imageClassName={cls("!absolute inset-0 w-full h-full object-cover !rounded-none", imageClassName)}
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
});
|
||||
|
||||
TestimonialCard.displayName = "TestimonialCard";
|
||||
|
||||
const TestimonialCardFive = ({
|
||||
testimonials,
|
||||
title,
|
||||
titleSegments,
|
||||
description,
|
||||
tag,
|
||||
tagIcon,
|
||||
buttons,
|
||||
textboxLayout,
|
||||
useInvertedBackground,
|
||||
ariaLabel = "Testimonials section",
|
||||
className = "",
|
||||
containerClassName = "",
|
||||
cardClassName = "",
|
||||
textBoxTitleClassName = "",
|
||||
textBoxTitleImageWrapperClassName = "",
|
||||
textBoxTitleImageClassName = "",
|
||||
textBoxDescriptionClassName = "",
|
||||
cardTagClassName = "",
|
||||
cardTitleClassName = "",
|
||||
cardQuoteClassName = "",
|
||||
cardAuthorClassName = "",
|
||||
cardAvatarWrapperClassName = "",
|
||||
cardAvatarClassName = "",
|
||||
cardNameClassName = "",
|
||||
cardDateClassName = "",
|
||||
cardImageClassName = "",
|
||||
carouselClassName = "",
|
||||
dotsClassName = "",
|
||||
textBoxClassName = "",
|
||||
textBoxTagClassName = "",
|
||||
textBoxButtonContainerClassName = "",
|
||||
textBoxButtonClassName = "",
|
||||
textBoxButtonTextClassName = "",
|
||||
}: TestimonialCardFiveProps) => {
|
||||
return (
|
||||
<FullWidthCarousel
|
||||
title={title}
|
||||
titleSegments={titleSegments}
|
||||
description={description}
|
||||
tag={tag}
|
||||
tagIcon={tagIcon}
|
||||
buttons={buttons}
|
||||
textboxLayout={textboxLayout}
|
||||
useInvertedBackground={useInvertedBackground}
|
||||
className={className}
|
||||
containerClassName={containerClassName}
|
||||
carouselClassName={carouselClassName}
|
||||
dotsClassName={dotsClassName}
|
||||
textBoxClassName={textBoxClassName}
|
||||
titleClassName={textBoxTitleClassName}
|
||||
titleImageWrapperClassName={textBoxTitleImageWrapperClassName}
|
||||
titleImageClassName={textBoxTitleImageClassName}
|
||||
descriptionClassName={textBoxDescriptionClassName}
|
||||
tagClassName={textBoxTagClassName}
|
||||
buttonContainerClassName={textBoxButtonContainerClassName}
|
||||
buttonClassName={textBoxButtonClassName}
|
||||
buttonTextClassName={textBoxButtonTextClassName}
|
||||
ariaLabel={ariaLabel}
|
||||
>
|
||||
{testimonials.map((testimonial, index) => (
|
||||
<TestimonialCard
|
||||
key={`${testimonial.id}-${index}`}
|
||||
testimonial={testimonial}
|
||||
useInvertedBackground={useInvertedBackground}
|
||||
cardClassName={cardClassName}
|
||||
tagClassName={cardTagClassName}
|
||||
titleClassName={cardTitleClassName}
|
||||
quoteClassName={cardQuoteClassName}
|
||||
authorClassName={cardAuthorClassName}
|
||||
avatarWrapperClassName={cardAvatarWrapperClassName}
|
||||
avatarClassName={cardAvatarClassName}
|
||||
nameClassName={cardNameClassName}
|
||||
dateClassName={cardDateClassName}
|
||||
imageClassName={cardImageClassName}
|
||||
/>
|
||||
))}
|
||||
</FullWidthCarousel>
|
||||
);
|
||||
};
|
||||
|
||||
TestimonialCardFive.displayName = "TestimonialCardFive";
|
||||
|
||||
export default TestimonialCardFive;
|
||||
213
src/components/sections/testimonial/TestimonialCardOne.tsx
Normal file
213
src/components/sections/testimonial/TestimonialCardOne.tsx
Normal file
@@ -0,0 +1,213 @@
|
||||
"use client";
|
||||
|
||||
import { memo } from "react";
|
||||
import Image from "next/image";
|
||||
import CardStack from "@/components/cardStack/CardStack";
|
||||
import { cls } from "@/lib/utils";
|
||||
import { Star } from "lucide-react";
|
||||
import type { LucideIcon } from "lucide-react";
|
||||
import type { ButtonConfig, CardAnimationTypeWith3D, GridVariant, TitleSegment, TextboxLayout, InvertedBackground } from "@/components/cardStack/types";
|
||||
|
||||
type Testimonial = {
|
||||
id: string;
|
||||
name: string;
|
||||
role: string;
|
||||
company: string;
|
||||
rating: number;
|
||||
imageSrc: string;
|
||||
imageAlt?: string;
|
||||
};
|
||||
|
||||
interface TestimonialCardOneProps {
|
||||
testimonials: Testimonial[];
|
||||
carouselMode?: "auto" | "buttons";
|
||||
uniformGridCustomHeightClasses?: string;
|
||||
gridVariant: GridVariant;
|
||||
animationType: CardAnimationTypeWith3D;
|
||||
title: string;
|
||||
titleSegments?: TitleSegment[];
|
||||
description: string;
|
||||
tag?: string;
|
||||
tagIcon?: LucideIcon;
|
||||
buttons?: ButtonConfig[];
|
||||
textboxLayout: TextboxLayout;
|
||||
useInvertedBackground: InvertedBackground;
|
||||
ariaLabel?: string;
|
||||
className?: string;
|
||||
containerClassName?: string;
|
||||
cardClassName?: string;
|
||||
textBoxTitleClassName?: string;
|
||||
textBoxTitleImageWrapperClassName?: string;
|
||||
textBoxTitleImageClassName?: string;
|
||||
textBoxDescriptionClassName?: string;
|
||||
imageClassName?: string;
|
||||
overlayClassName?: string;
|
||||
ratingClassName?: string;
|
||||
nameClassName?: string;
|
||||
roleClassName?: string;
|
||||
companyClassName?: string;
|
||||
gridClassName?: string;
|
||||
carouselClassName?: string;
|
||||
controlsClassName?: string;
|
||||
textBoxClassName?: string;
|
||||
textBoxTagClassName?: string;
|
||||
textBoxButtonContainerClassName?: string;
|
||||
textBoxButtonClassName?: string;
|
||||
textBoxButtonTextClassName?: string;
|
||||
}
|
||||
|
||||
interface TestimonialCardProps {
|
||||
testimonial: Testimonial;
|
||||
cardClassName?: string;
|
||||
imageClassName?: string;
|
||||
overlayClassName?: string;
|
||||
ratingClassName?: string;
|
||||
nameClassName?: string;
|
||||
roleClassName?: string;
|
||||
companyClassName?: string;
|
||||
}
|
||||
|
||||
const TestimonialCard = memo(({
|
||||
testimonial,
|
||||
cardClassName = "",
|
||||
imageClassName = "",
|
||||
overlayClassName = "",
|
||||
ratingClassName = "",
|
||||
nameClassName = "",
|
||||
roleClassName = "",
|
||||
companyClassName = "",
|
||||
}: TestimonialCardProps) => {
|
||||
return (
|
||||
<div className={cls("relative h-full rounded-theme-capped overflow-hidden group", cardClassName)}>
|
||||
<Image
|
||||
src={testimonial.imageSrc}
|
||||
alt={testimonial.imageAlt || testimonial.name}
|
||||
width={800}
|
||||
height={800}
|
||||
className={cls("relative z-1 w-full h-full object-cover!", imageClassName)}
|
||||
unoptimized={testimonial.imageSrc.startsWith('http') || testimonial.imageSrc.startsWith('//')}
|
||||
aria-hidden={testimonial.imageAlt === ""}
|
||||
/>
|
||||
|
||||
<div className={cls("!absolute z-1 bottom-6 left-6 right-6 card backdrop-blur-xs p-6 flex flex-col gap-3 rounded-theme-capped", overlayClassName)}>
|
||||
<div className={cls("relative z-1 flex gap-1", ratingClassName)}>
|
||||
{Array.from({ length: 5 }).map((_, index) => (
|
||||
<Star
|
||||
key={index}
|
||||
className={cls(
|
||||
"h-5 w-auto text-accent",
|
||||
index < testimonial.rating ? "fill-accent" : "fill-transparent"
|
||||
)}
|
||||
strokeWidth={1.5}
|
||||
/>
|
||||
))}
|
||||
</div>
|
||||
|
||||
<h3 className={cls("relative z-1 text-2xl font-medium text-foreground leading-[1.1] mt-1", nameClassName)}>
|
||||
{testimonial.name}
|
||||
</h3>
|
||||
|
||||
<div className="relative z-1 flex flex-col gap-1">
|
||||
<p className={cls("text-base text-foreground leading-[1.1]", roleClassName)}>
|
||||
{testimonial.role}
|
||||
</p>
|
||||
<p className={cls("text-base text-foreground leading-[1.1]", companyClassName)}>
|
||||
{testimonial.company}
|
||||
</p>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
});
|
||||
|
||||
TestimonialCard.displayName = "TestimonialCard";
|
||||
|
||||
const TestimonialCardOne = ({
|
||||
testimonials,
|
||||
carouselMode = "buttons",
|
||||
uniformGridCustomHeightClasses = "min-h-95 2xl:min-h-105",
|
||||
gridVariant,
|
||||
animationType,
|
||||
title,
|
||||
titleSegments,
|
||||
description,
|
||||
tag,
|
||||
tagIcon,
|
||||
buttons,
|
||||
textboxLayout,
|
||||
useInvertedBackground,
|
||||
ariaLabel = "Testimonials section",
|
||||
className = "",
|
||||
containerClassName = "",
|
||||
cardClassName = "",
|
||||
textBoxTitleClassName = "",
|
||||
textBoxTitleImageWrapperClassName = "",
|
||||
textBoxTitleImageClassName = "",
|
||||
textBoxDescriptionClassName = "",
|
||||
imageClassName = "",
|
||||
overlayClassName = "",
|
||||
ratingClassName = "",
|
||||
nameClassName = "",
|
||||
roleClassName = "",
|
||||
companyClassName = "",
|
||||
gridClassName = "",
|
||||
carouselClassName = "",
|
||||
controlsClassName = "",
|
||||
textBoxClassName = "",
|
||||
textBoxTagClassName = "",
|
||||
textBoxButtonContainerClassName = "",
|
||||
textBoxButtonClassName = "",
|
||||
textBoxButtonTextClassName = "",
|
||||
}: TestimonialCardOneProps) => {
|
||||
return (
|
||||
<CardStack
|
||||
mode={carouselMode}
|
||||
gridVariant={gridVariant}
|
||||
uniformGridCustomHeightClasses={uniformGridCustomHeightClasses}
|
||||
animationType={animationType}
|
||||
supports3DAnimation={true}
|
||||
|
||||
title={title}
|
||||
titleSegments={titleSegments}
|
||||
description={description}
|
||||
tag={tag}
|
||||
tagIcon={tagIcon}
|
||||
buttons={buttons}
|
||||
textboxLayout={textboxLayout}
|
||||
useInvertedBackground={useInvertedBackground}
|
||||
className={className}
|
||||
containerClassName={containerClassName}
|
||||
gridClassName={gridClassName}
|
||||
carouselClassName={carouselClassName}
|
||||
controlsClassName={controlsClassName}
|
||||
textBoxClassName={textBoxClassName}
|
||||
titleClassName={textBoxTitleClassName}
|
||||
titleImageWrapperClassName={textBoxTitleImageWrapperClassName}
|
||||
titleImageClassName={textBoxTitleImageClassName}
|
||||
descriptionClassName={textBoxDescriptionClassName}
|
||||
tagClassName={textBoxTagClassName}
|
||||
buttonContainerClassName={textBoxButtonContainerClassName}
|
||||
buttonClassName={textBoxButtonClassName}
|
||||
buttonTextClassName={textBoxButtonTextClassName}
|
||||
ariaLabel={ariaLabel}
|
||||
>
|
||||
{testimonials.map((testimonial, index) => (
|
||||
<TestimonialCard
|
||||
key={`${testimonial.id}-${index}`}
|
||||
testimonial={testimonial}
|
||||
cardClassName={cardClassName}
|
||||
imageClassName={imageClassName}
|
||||
overlayClassName={overlayClassName}
|
||||
ratingClassName={ratingClassName}
|
||||
nameClassName={nameClassName}
|
||||
roleClassName={roleClassName}
|
||||
companyClassName={companyClassName}
|
||||
/>
|
||||
))}
|
||||
</CardStack>
|
||||
);
|
||||
};
|
||||
|
||||
TestimonialCardOne.displayName = "TestimonialCardOne";
|
||||
|
||||
export default TestimonialCardOne;
|
||||
197
src/components/sections/testimonial/TestimonialCardSix.tsx
Normal file
197
src/components/sections/testimonial/TestimonialCardSix.tsx
Normal file
@@ -0,0 +1,197 @@
|
||||
"use client";
|
||||
|
||||
import { memo } from "react";
|
||||
import AutoCarousel from "@/components/cardStack/layouts/carousels/AutoCarousel";
|
||||
import TestimonialAuthor from "@/components/shared/TestimonialAuthor";
|
||||
import { cls, shouldUseInvertedText } from "@/lib/utils";
|
||||
import { useTheme } from "@/providers/themeProvider/ThemeProvider";
|
||||
import { Quote } from "lucide-react";
|
||||
import type { LucideIcon } from "lucide-react";
|
||||
import type { CardAnimationType, ButtonConfig, TitleSegment, TextboxLayout, InvertedBackground } from "@/components/cardStack/types";
|
||||
|
||||
type Testimonial = {
|
||||
id: string;
|
||||
name: string;
|
||||
handle: string;
|
||||
testimonial: string;
|
||||
imageSrc?: string;
|
||||
imageAlt?: string;
|
||||
icon?: LucideIcon;
|
||||
};
|
||||
|
||||
interface TestimonialCardSixProps {
|
||||
testimonials: Testimonial[];
|
||||
animationType: CardAnimationType;
|
||||
title: string;
|
||||
titleSegments?: TitleSegment[];
|
||||
description: string;
|
||||
textboxLayout: TextboxLayout;
|
||||
useInvertedBackground: InvertedBackground;
|
||||
tag?: string;
|
||||
tagIcon?: LucideIcon;
|
||||
buttons?: ButtonConfig[];
|
||||
speed?: number;
|
||||
topMarqueeDirection?: "left" | "right";
|
||||
ariaLabel?: string;
|
||||
className?: string;
|
||||
containerClassName?: string;
|
||||
carouselClassName?: string;
|
||||
bottomCarouselClassName?: string;
|
||||
cardClassName?: string;
|
||||
testimonialClassName?: string;
|
||||
imageWrapperClassName?: string;
|
||||
imageClassName?: string;
|
||||
iconClassName?: string;
|
||||
nameClassName?: string;
|
||||
handleClassName?: string;
|
||||
textBoxClassName?: string;
|
||||
textBoxTitleClassName?: string;
|
||||
textBoxTitleImageWrapperClassName?: string;
|
||||
textBoxTitleImageClassName?: string;
|
||||
textBoxDescriptionClassName?: string;
|
||||
textBoxTagClassName?: string;
|
||||
textBoxButtonContainerClassName?: string;
|
||||
textBoxButtonClassName?: string;
|
||||
textBoxButtonTextClassName?: string;
|
||||
}
|
||||
|
||||
interface TestimonialCardProps {
|
||||
testimonial: Testimonial;
|
||||
useInvertedBackground: "noInvert" | "invertDefault";
|
||||
cardClassName?: string;
|
||||
testimonialClassName?: string;
|
||||
imageWrapperClassName?: string;
|
||||
imageClassName?: string;
|
||||
iconClassName?: string;
|
||||
nameClassName?: string;
|
||||
handleClassName?: string;
|
||||
}
|
||||
|
||||
const TestimonialCard = memo(({
|
||||
testimonial,
|
||||
useInvertedBackground,
|
||||
cardClassName = "",
|
||||
testimonialClassName = "",
|
||||
imageWrapperClassName = "",
|
||||
imageClassName = "",
|
||||
iconClassName = "",
|
||||
nameClassName = "",
|
||||
handleClassName = "",
|
||||
}: TestimonialCardProps) => {
|
||||
const Icon = testimonial.icon || Quote;
|
||||
const theme = useTheme();
|
||||
const shouldUseLightText = shouldUseInvertedText(useInvertedBackground, theme.cardStyle);
|
||||
|
||||
return (
|
||||
<div className={cls("relative h-full card rounded-theme-capped p-6 min-h-0 flex flex-col gap-10", cardClassName)}>
|
||||
<p className={cls("relative z-1 text-lg leading-tight line-clamp-2", shouldUseLightText ? "text-background" : "text-foreground", testimonialClassName)}>
|
||||
{testimonial.testimonial}
|
||||
</p>
|
||||
|
||||
<TestimonialAuthor
|
||||
name={testimonial.name}
|
||||
subtitle={testimonial.handle}
|
||||
imageSrc={testimonial.imageSrc}
|
||||
imageAlt={testimonial.imageAlt}
|
||||
icon={Icon}
|
||||
useInvertedBackground={useInvertedBackground}
|
||||
imageWrapperClassName={imageWrapperClassName}
|
||||
imageClassName={imageClassName}
|
||||
iconClassName={iconClassName}
|
||||
nameClassName={nameClassName}
|
||||
subtitleClassName={handleClassName}
|
||||
/>
|
||||
</div>
|
||||
);
|
||||
});
|
||||
|
||||
TestimonialCard.displayName = "TestimonialCard";
|
||||
|
||||
const TestimonialCardSix = ({
|
||||
testimonials,
|
||||
animationType,
|
||||
title,
|
||||
titleSegments,
|
||||
description,
|
||||
textboxLayout,
|
||||
useInvertedBackground,
|
||||
tag,
|
||||
tagIcon,
|
||||
buttons,
|
||||
speed = 40,
|
||||
topMarqueeDirection = "left",
|
||||
ariaLabel = "Testimonials section",
|
||||
className = "",
|
||||
containerClassName = "",
|
||||
carouselClassName = "",
|
||||
bottomCarouselClassName = "",
|
||||
cardClassName = "",
|
||||
testimonialClassName = "",
|
||||
imageWrapperClassName = "",
|
||||
imageClassName = "",
|
||||
iconClassName = "",
|
||||
nameClassName = "",
|
||||
handleClassName = "",
|
||||
textBoxClassName = "",
|
||||
textBoxTitleClassName = "",
|
||||
textBoxTitleImageWrapperClassName = "",
|
||||
textBoxTitleImageClassName = "",
|
||||
textBoxDescriptionClassName = "",
|
||||
textBoxTagClassName = "",
|
||||
textBoxButtonContainerClassName = "",
|
||||
textBoxButtonClassName = "",
|
||||
textBoxButtonTextClassName = "",
|
||||
}: TestimonialCardSixProps) => {
|
||||
return (
|
||||
<AutoCarousel
|
||||
speed={speed}
|
||||
uniformGridCustomHeightClasses="min-h-none"
|
||||
animationType={animationType}
|
||||
title={title}
|
||||
titleSegments={titleSegments}
|
||||
description={description}
|
||||
tag={tag}
|
||||
tagIcon={tagIcon}
|
||||
buttons={buttons}
|
||||
textboxLayout={textboxLayout}
|
||||
useInvertedBackground={useInvertedBackground}
|
||||
showTextBox={true}
|
||||
dualMarquee={true}
|
||||
topMarqueeDirection={topMarqueeDirection}
|
||||
carouselClassName={carouselClassName}
|
||||
bottomCarouselClassName={bottomCarouselClassName}
|
||||
containerClassName={containerClassName}
|
||||
className={className}
|
||||
textBoxClassName={textBoxClassName}
|
||||
titleClassName={textBoxTitleClassName}
|
||||
titleImageWrapperClassName={textBoxTitleImageWrapperClassName}
|
||||
titleImageClassName={textBoxTitleImageClassName}
|
||||
descriptionClassName={textBoxDescriptionClassName}
|
||||
tagClassName={textBoxTagClassName}
|
||||
buttonContainerClassName={textBoxButtonContainerClassName}
|
||||
buttonClassName={textBoxButtonClassName}
|
||||
buttonTextClassName={textBoxButtonTextClassName}
|
||||
ariaLabel={ariaLabel}
|
||||
itemClassName="w-60! md:w-carousel-item-3! xl:w-carousel-item-4!"
|
||||
>
|
||||
{testimonials.map((testimonial, index) => (
|
||||
<TestimonialCard
|
||||
key={`${testimonial.id}-${index}`}
|
||||
testimonial={testimonial}
|
||||
useInvertedBackground={useInvertedBackground}
|
||||
cardClassName={cardClassName}
|
||||
testimonialClassName={testimonialClassName}
|
||||
imageWrapperClassName={imageWrapperClassName}
|
||||
imageClassName={imageClassName}
|
||||
iconClassName={iconClassName}
|
||||
nameClassName={nameClassName}
|
||||
handleClassName={handleClassName}
|
||||
/>
|
||||
))}
|
||||
</AutoCarousel>
|
||||
);
|
||||
};
|
||||
|
||||
TestimonialCardSix.displayName = "TestimonialCardSix";
|
||||
|
||||
export default TestimonialCardSix;
|
||||
234
src/components/sections/testimonial/TestimonialCardSixteen.tsx
Normal file
234
src/components/sections/testimonial/TestimonialCardSixteen.tsx
Normal file
@@ -0,0 +1,234 @@
|
||||
"use client";
|
||||
|
||||
import { memo } from "react";
|
||||
import Image from "next/image";
|
||||
import CardStack from "@/components/cardStack/CardStack";
|
||||
import { cls } from "@/lib/utils";
|
||||
import { Star } from "lucide-react";
|
||||
import type { LucideIcon } from "lucide-react";
|
||||
import type { ButtonConfig, CardAnimationTypeWith3D, TitleSegment, TextboxLayout, InvertedBackground } from "@/components/cardStack/types";
|
||||
|
||||
type Testimonial = {
|
||||
id: string;
|
||||
name: string;
|
||||
role: string;
|
||||
company: string;
|
||||
rating: number;
|
||||
imageSrc: string;
|
||||
imageAlt?: string;
|
||||
};
|
||||
|
||||
type KpiItem = {
|
||||
value: string;
|
||||
label: string;
|
||||
};
|
||||
|
||||
interface TestimonialCardSixteenProps {
|
||||
testimonials: Testimonial[];
|
||||
kpiItems: [KpiItem, KpiItem, KpiItem];
|
||||
carouselMode?: "auto" | "buttons";
|
||||
uniformGridCustomHeightClasses?: string;
|
||||
animationType: CardAnimationTypeWith3D;
|
||||
title: string;
|
||||
titleSegments?: TitleSegment[];
|
||||
description: string;
|
||||
tag?: string;
|
||||
tagIcon?: LucideIcon;
|
||||
buttons?: ButtonConfig[];
|
||||
textboxLayout: TextboxLayout;
|
||||
useInvertedBackground: InvertedBackground;
|
||||
ariaLabel?: string;
|
||||
className?: string;
|
||||
containerClassName?: string;
|
||||
cardClassName?: string;
|
||||
textBoxTitleClassName?: string;
|
||||
textBoxTitleImageWrapperClassName?: string;
|
||||
textBoxTitleImageClassName?: string;
|
||||
textBoxDescriptionClassName?: string;
|
||||
imageClassName?: string;
|
||||
overlayClassName?: string;
|
||||
ratingClassName?: string;
|
||||
nameClassName?: string;
|
||||
roleClassName?: string;
|
||||
companyClassName?: string;
|
||||
gridClassName?: string;
|
||||
carouselClassName?: string;
|
||||
controlsClassName?: string;
|
||||
textBoxClassName?: string;
|
||||
textBoxTagClassName?: string;
|
||||
textBoxButtonContainerClassName?: string;
|
||||
textBoxButtonClassName?: string;
|
||||
textBoxButtonTextClassName?: string;
|
||||
}
|
||||
|
||||
interface TestimonialCardProps {
|
||||
testimonial: Testimonial;
|
||||
cardClassName?: string;
|
||||
imageClassName?: string;
|
||||
overlayClassName?: string;
|
||||
ratingClassName?: string;
|
||||
nameClassName?: string;
|
||||
roleClassName?: string;
|
||||
companyClassName?: string;
|
||||
}
|
||||
|
||||
const TestimonialCard = memo(({
|
||||
testimonial,
|
||||
cardClassName = "",
|
||||
imageClassName = "",
|
||||
overlayClassName = "",
|
||||
ratingClassName = "",
|
||||
nameClassName = "",
|
||||
roleClassName = "",
|
||||
companyClassName = "",
|
||||
}: TestimonialCardProps) => {
|
||||
return (
|
||||
<div className={cls("relative h-full aspect-[8/10] rounded-theme-capped overflow-hidden group", cardClassName)}>
|
||||
<Image
|
||||
src={testimonial.imageSrc}
|
||||
alt={testimonial.imageAlt || testimonial.name}
|
||||
width={800}
|
||||
height={800}
|
||||
className={cls("relative z-1 w-full h-full object-cover!", imageClassName)}
|
||||
unoptimized={testimonial.imageSrc.startsWith('http') || testimonial.imageSrc.startsWith('//')}
|
||||
aria-hidden={testimonial.imageAlt === ""}
|
||||
/>
|
||||
|
||||
<div className={cls("!absolute z-1 bottom-6 left-6 right-6 card backdrop-blur-xs p-6 flex flex-col gap-3 rounded-theme-capped", overlayClassName)}>
|
||||
<div className={cls("relative z-1 flex gap-1", ratingClassName)}>
|
||||
{Array.from({ length: 5 }).map((_, index) => (
|
||||
<Star
|
||||
key={index}
|
||||
className={cls(
|
||||
"h-5 w-auto text-accent",
|
||||
index < testimonial.rating ? "fill-accent" : "fill-transparent"
|
||||
)}
|
||||
strokeWidth={1.5}
|
||||
/>
|
||||
))}
|
||||
</div>
|
||||
|
||||
<h3 className={cls("relative z-1 text-2xl font-medium text-foreground leading-[1.1] mt-1", nameClassName)}>
|
||||
{testimonial.name}
|
||||
</h3>
|
||||
|
||||
<div className="relative z-1 flex flex-col gap-1">
|
||||
<p className={cls("text-base text-foreground leading-[1.1]", roleClassName)}>
|
||||
{testimonial.role}
|
||||
</p>
|
||||
<p className={cls("text-base text-foreground leading-[1.1]", companyClassName)}>
|
||||
{testimonial.company}
|
||||
</p>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
});
|
||||
|
||||
TestimonialCard.displayName = "TestimonialCard";
|
||||
|
||||
const TestimonialCardSixteen = ({
|
||||
testimonials,
|
||||
kpiItems,
|
||||
carouselMode = "buttons",
|
||||
uniformGridCustomHeightClasses = "min-h-85 2xl:min-h-105",
|
||||
animationType,
|
||||
title,
|
||||
titleSegments,
|
||||
description,
|
||||
tag,
|
||||
tagIcon,
|
||||
buttons,
|
||||
textboxLayout,
|
||||
useInvertedBackground,
|
||||
ariaLabel = "Testimonials section",
|
||||
className = "",
|
||||
containerClassName = "",
|
||||
cardClassName = "",
|
||||
textBoxTitleClassName = "",
|
||||
textBoxTitleImageWrapperClassName = "",
|
||||
textBoxTitleImageClassName = "",
|
||||
textBoxDescriptionClassName = "",
|
||||
imageClassName = "",
|
||||
overlayClassName = "",
|
||||
ratingClassName = "",
|
||||
nameClassName = "",
|
||||
roleClassName = "",
|
||||
companyClassName = "",
|
||||
gridClassName = "",
|
||||
carouselClassName = "",
|
||||
controlsClassName = "",
|
||||
textBoxClassName = "",
|
||||
textBoxTagClassName = "",
|
||||
textBoxButtonContainerClassName = "",
|
||||
textBoxButtonClassName = "",
|
||||
textBoxButtonTextClassName = "",
|
||||
}: TestimonialCardSixteenProps) => {
|
||||
const kpiSection = (
|
||||
<div className="card rounded-theme-capped p-8 md:py-16 flex flex-col md:flex-row items-center justify-between">
|
||||
{kpiItems.map((item, index) => (
|
||||
<div key={index} className="flex flex-col md:flex-row items-center w-full md:flex-1">
|
||||
<div className="flex flex-col items-center text-center flex-1 py-4 md:py-0 gap-1">
|
||||
<h3 className="text-5xl font-medium text-foreground">{item.value}</h3>
|
||||
<p className="text-base text-foreground">{item.label}</p>
|
||||
</div>
|
||||
{index < 2 && (
|
||||
<div className="w-full h-px md:h-[calc(var(--text-5xl)+var(--text-base))] md:w-px bg-foreground" />
|
||||
)}
|
||||
</div>
|
||||
))}
|
||||
</div>
|
||||
);
|
||||
|
||||
return (
|
||||
<CardStack
|
||||
mode={carouselMode}
|
||||
gridVariant="uniform-all-items-equal"
|
||||
uniformGridCustomHeightClasses={uniformGridCustomHeightClasses}
|
||||
animationType={animationType}
|
||||
supports3DAnimation={true}
|
||||
title={title}
|
||||
titleSegments={titleSegments}
|
||||
description={description}
|
||||
tag={tag}
|
||||
tagIcon={tagIcon}
|
||||
buttons={buttons}
|
||||
textboxLayout={textboxLayout}
|
||||
useInvertedBackground={useInvertedBackground}
|
||||
bottomContent={kpiSection}
|
||||
className={className}
|
||||
containerClassName={containerClassName}
|
||||
gridClassName={gridClassName}
|
||||
carouselClassName={carouselClassName}
|
||||
controlsClassName={controlsClassName}
|
||||
textBoxClassName={textBoxClassName}
|
||||
titleClassName={textBoxTitleClassName}
|
||||
titleImageWrapperClassName={textBoxTitleImageWrapperClassName}
|
||||
titleImageClassName={textBoxTitleImageClassName}
|
||||
descriptionClassName={textBoxDescriptionClassName}
|
||||
tagClassName={textBoxTagClassName}
|
||||
buttonContainerClassName={textBoxButtonContainerClassName}
|
||||
buttonClassName={textBoxButtonClassName}
|
||||
buttonTextClassName={textBoxButtonTextClassName}
|
||||
ariaLabel={ariaLabel}
|
||||
>
|
||||
{testimonials.map((testimonial, index) => (
|
||||
<TestimonialCard
|
||||
key={`${testimonial.id}-${index}`}
|
||||
testimonial={testimonial}
|
||||
cardClassName={cardClassName}
|
||||
imageClassName={imageClassName}
|
||||
overlayClassName={overlayClassName}
|
||||
ratingClassName={ratingClassName}
|
||||
nameClassName={nameClassName}
|
||||
roleClassName={roleClassName}
|
||||
companyClassName={companyClassName}
|
||||
/>
|
||||
))}
|
||||
</CardStack>
|
||||
);
|
||||
};
|
||||
|
||||
TestimonialCardSixteen.displayName = "TestimonialCardSixteen";
|
||||
|
||||
export default TestimonialCardSixteen;
|
||||
228
src/components/sections/testimonial/TestimonialCardTen.tsx
Normal file
228
src/components/sections/testimonial/TestimonialCardTen.tsx
Normal file
@@ -0,0 +1,228 @@
|
||||
"use client";
|
||||
|
||||
import { useState } from "react";
|
||||
import { ArrowLeft, ArrowRight } from "lucide-react";
|
||||
import CardStackTextBox from "@/components/cardStack/CardStackTextBox";
|
||||
import MediaContent from "@/components/shared/MediaContent";
|
||||
import AnimationContainer from "@/components/sections/AnimationContainer";
|
||||
import { cls, shouldUseInvertedText } from "@/lib/utils";
|
||||
import { useTheme } from "@/providers/themeProvider/ThemeProvider";
|
||||
import type { LucideIcon } from "lucide-react";
|
||||
import type { ButtonConfig, TitleSegment } from "@/components/cardStack/types";
|
||||
import type { TextboxLayout, InvertedBackground } from "@/providers/themeProvider/config/constants";
|
||||
|
||||
interface Testimonial {
|
||||
id: string;
|
||||
title: string;
|
||||
quote: string;
|
||||
name: string;
|
||||
role: string;
|
||||
imageSrc?: string;
|
||||
videoSrc?: string;
|
||||
imageAlt?: string;
|
||||
videoAriaLabel?: string;
|
||||
}
|
||||
|
||||
interface TestimonialCardTenProps {
|
||||
testimonials: Testimonial[];
|
||||
title: string;
|
||||
titleSegments?: TitleSegment[];
|
||||
description: string;
|
||||
tag?: string;
|
||||
tagIcon?: LucideIcon;
|
||||
buttons?: ButtonConfig[];
|
||||
textboxLayout: TextboxLayout;
|
||||
useInvertedBackground: InvertedBackground;
|
||||
ariaLabel?: string;
|
||||
className?: string;
|
||||
containerClassName?: string;
|
||||
textBoxTitleClassName?: string;
|
||||
textBoxDescriptionClassName?: string;
|
||||
textBoxClassName?: string;
|
||||
textBoxTagClassName?: string;
|
||||
textBoxButtonContainerClassName?: string;
|
||||
textBoxButtonClassName?: string;
|
||||
textBoxButtonTextClassName?: string;
|
||||
titleImageWrapperClassName?: string;
|
||||
titleImageClassName?: string;
|
||||
contentClassName?: string;
|
||||
quoteCardClassName?: string;
|
||||
testimonialTitleClassName?: string;
|
||||
quoteClassName?: string;
|
||||
nameClassName?: string;
|
||||
roleClassName?: string;
|
||||
navigationClassName?: string;
|
||||
navigationButtonClassName?: string;
|
||||
mediaCardClassName?: string;
|
||||
mediaClassName?: string;
|
||||
}
|
||||
|
||||
const TestimonialCardTen = ({
|
||||
testimonials,
|
||||
title,
|
||||
titleSegments,
|
||||
description,
|
||||
tag,
|
||||
tagIcon,
|
||||
buttons,
|
||||
textboxLayout,
|
||||
useInvertedBackground,
|
||||
ariaLabel = "Testimonials section",
|
||||
className = "",
|
||||
containerClassName = "",
|
||||
textBoxTitleClassName = "",
|
||||
textBoxDescriptionClassName = "",
|
||||
textBoxClassName = "",
|
||||
textBoxTagClassName = "",
|
||||
textBoxButtonContainerClassName = "",
|
||||
textBoxButtonClassName = "",
|
||||
textBoxButtonTextClassName = "",
|
||||
titleImageWrapperClassName = "",
|
||||
titleImageClassName = "",
|
||||
contentClassName = "",
|
||||
quoteCardClassName = "",
|
||||
testimonialTitleClassName = "",
|
||||
quoteClassName = "",
|
||||
nameClassName = "",
|
||||
roleClassName = "",
|
||||
navigationClassName = "",
|
||||
navigationButtonClassName = "",
|
||||
mediaCardClassName = "",
|
||||
mediaClassName = "",
|
||||
}: TestimonialCardTenProps) => {
|
||||
const [activeIndex, setActiveIndex] = useState(0);
|
||||
const theme = useTheme();
|
||||
const shouldUseLightText = shouldUseInvertedText(useInvertedBackground, theme.cardStyle);
|
||||
|
||||
const handlePrev = () => {
|
||||
setActiveIndex((prev) => (prev === 0 ? testimonials.length - 1 : prev - 1));
|
||||
};
|
||||
|
||||
const handleNext = () => {
|
||||
setActiveIndex((prev) => (prev === testimonials.length - 1 ? 0 : prev + 1));
|
||||
};
|
||||
|
||||
const activeTestimonial = testimonials[activeIndex];
|
||||
|
||||
return (
|
||||
<section
|
||||
aria-label={ariaLabel}
|
||||
className={cls("relative py-20 w-full", useInvertedBackground === "invertDefault" && "bg-foreground", className)}
|
||||
>
|
||||
<div className={cls("w-content-width mx-auto flex flex-col gap-10", containerClassName)}>
|
||||
<CardStackTextBox
|
||||
title={title}
|
||||
titleSegments={titleSegments}
|
||||
description={description}
|
||||
tag={tag}
|
||||
tagIcon={tagIcon}
|
||||
buttons={buttons}
|
||||
textboxLayout={textboxLayout}
|
||||
useInvertedBackground={useInvertedBackground}
|
||||
textBoxClassName={textBoxClassName}
|
||||
titleClassName={textBoxTitleClassName}
|
||||
titleImageWrapperClassName={titleImageWrapperClassName}
|
||||
titleImageClassName={titleImageClassName}
|
||||
descriptionClassName={textBoxDescriptionClassName}
|
||||
tagClassName={textBoxTagClassName}
|
||||
buttonContainerClassName={textBoxButtonContainerClassName}
|
||||
buttonClassName={textBoxButtonClassName}
|
||||
buttonTextClassName={textBoxButtonTextClassName}
|
||||
/>
|
||||
|
||||
<div className={cls(
|
||||
"grid grid-cols-1 gap-6 md:grid-cols-2",
|
||||
contentClassName
|
||||
)}>
|
||||
<div className={cls(
|
||||
"flex flex-col justify-between gap-6 card rounded-theme-capped p-6 md:p-10",
|
||||
quoteCardClassName
|
||||
)}>
|
||||
<AnimationContainer key={activeIndex} animationType="fade" className="relative z-1 flex flex-col justify-between gap-6 flex-1">
|
||||
<div className="flex flex-col gap-3">
|
||||
<h3 className={cls(
|
||||
"text-2xl md:text-3xl font-medium leading-tight",
|
||||
shouldUseLightText ? "text-background" : "text-foreground",
|
||||
testimonialTitleClassName
|
||||
)}>
|
||||
{activeTestimonial.title}
|
||||
</h3>
|
||||
|
||||
<blockquote className={cls(
|
||||
"text-base md:text-lg leading-tight",
|
||||
shouldUseLightText ? "text-background/75" : "text-foreground/75",
|
||||
quoteClassName
|
||||
)}>
|
||||
“{activeTestimonial.quote}”
|
||||
</blockquote>
|
||||
</div>
|
||||
|
||||
<div className={cls(
|
||||
"w-full h-px md:hidden",
|
||||
shouldUseLightText ? "bg-background/20" : "bg-foreground/20"
|
||||
)} />
|
||||
|
||||
<div className={cls("flex items-center justify-between gap-4", navigationClassName)}>
|
||||
<div className="flex flex-col">
|
||||
<p className={cls(
|
||||
"text-base leading-tight font-medium",
|
||||
shouldUseLightText ? "text-background" : "text-foreground",
|
||||
nameClassName
|
||||
)}>
|
||||
{activeTestimonial.name}
|
||||
</p>
|
||||
<p className={cls(
|
||||
"text-sm leading-tight",
|
||||
shouldUseLightText ? "text-background/75" : "text-foreground/75",
|
||||
roleClassName
|
||||
)}>
|
||||
{activeTestimonial.role}
|
||||
</p>
|
||||
</div>
|
||||
|
||||
<div className="flex gap-4">
|
||||
<button
|
||||
onClick={handlePrev}
|
||||
aria-label="Previous testimonial"
|
||||
className={cls(
|
||||
"relative cursor-pointer h-9 w-auto aspect-square rounded-theme flex items-center justify-center primary-button",
|
||||
navigationButtonClassName
|
||||
)}
|
||||
>
|
||||
<ArrowLeft className="h-4/10 w-4/10 text-background" strokeWidth={1.5} />
|
||||
</button>
|
||||
<button
|
||||
onClick={handleNext}
|
||||
aria-label="Next testimonial"
|
||||
className={cls(
|
||||
"relative cursor-pointer h-9 w-auto aspect-square rounded-theme flex items-center justify-center primary-button",
|
||||
navigationButtonClassName
|
||||
)}
|
||||
>
|
||||
<ArrowRight className="h-4/10 w-4/10 text-background" strokeWidth={1.5} />
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
</AnimationContainer>
|
||||
</div>
|
||||
|
||||
<div className={cls("rounded-theme-capped overflow-hidden", mediaCardClassName)}>
|
||||
<AnimationContainer key={activeIndex} animationType="fade">
|
||||
<MediaContent
|
||||
imageSrc={activeTestimonial.imageSrc}
|
||||
videoSrc={activeTestimonial.videoSrc}
|
||||
imageAlt={activeTestimonial.imageAlt || activeTestimonial.name}
|
||||
videoAriaLabel={activeTestimonial.videoAriaLabel || activeTestimonial.name}
|
||||
imageClassName={cls("w-full h-full aspect-square object-cover", mediaClassName)}
|
||||
/>
|
||||
</AnimationContainer>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</section>
|
||||
);
|
||||
};
|
||||
|
||||
TestimonialCardTen.displayName = "TestimonialCardTen";
|
||||
|
||||
export default TestimonialCardTen;
|
||||
234
src/components/sections/testimonial/TestimonialCardThirteen.tsx
Normal file
234
src/components/sections/testimonial/TestimonialCardThirteen.tsx
Normal file
@@ -0,0 +1,234 @@
|
||||
"use client";
|
||||
|
||||
import { memo } from "react";
|
||||
import CardStack from "@/components/cardStack/CardStack";
|
||||
import TestimonialAuthor from "@/components/shared/TestimonialAuthor";
|
||||
import { cls, shouldUseInvertedText } from "@/lib/utils";
|
||||
import { useTheme } from "@/providers/themeProvider/ThemeProvider";
|
||||
import { Quote, Star } from "lucide-react";
|
||||
import type { LucideIcon } from "lucide-react";
|
||||
import type { ButtonConfig, CardAnimationTypeWith3D, TitleSegment, TextboxLayout, InvertedBackground } from "@/components/cardStack/types";
|
||||
|
||||
type Testimonial = {
|
||||
id: string;
|
||||
name: string;
|
||||
handle: string;
|
||||
testimonial: string;
|
||||
rating: number;
|
||||
imageSrc?: string;
|
||||
imageAlt?: string;
|
||||
icon?: LucideIcon;
|
||||
};
|
||||
|
||||
interface TestimonialCardThirteenProps {
|
||||
testimonials: Testimonial[];
|
||||
showRating: boolean;
|
||||
carouselMode?: "auto" | "buttons";
|
||||
uniformGridCustomHeightClasses?: string;
|
||||
animationType: CardAnimationTypeWith3D;
|
||||
title: string;
|
||||
titleSegments?: TitleSegment[];
|
||||
description: string;
|
||||
tag?: string;
|
||||
tagIcon?: LucideIcon;
|
||||
buttons?: ButtonConfig[];
|
||||
textboxLayout: TextboxLayout;
|
||||
useInvertedBackground: InvertedBackground;
|
||||
ariaLabel?: string;
|
||||
className?: string;
|
||||
containerClassName?: string;
|
||||
cardClassName?: string;
|
||||
textBoxTitleClassName?: string;
|
||||
textBoxTitleImageWrapperClassName?: string;
|
||||
textBoxTitleImageClassName?: string;
|
||||
textBoxDescriptionClassName?: string;
|
||||
imageWrapperClassName?: string;
|
||||
imageClassName?: string;
|
||||
iconClassName?: string;
|
||||
nameClassName?: string;
|
||||
handleClassName?: string;
|
||||
testimonialClassName?: string;
|
||||
ratingClassName?: string;
|
||||
contentWrapperClassName?: string;
|
||||
gridClassName?: string;
|
||||
carouselClassName?: string;
|
||||
controlsClassName?: string;
|
||||
textBoxClassName?: string;
|
||||
textBoxTagClassName?: string;
|
||||
textBoxButtonContainerClassName?: string;
|
||||
textBoxButtonClassName?: string;
|
||||
textBoxButtonTextClassName?: string;
|
||||
}
|
||||
|
||||
interface TestimonialCardProps {
|
||||
testimonial: Testimonial;
|
||||
showRating: boolean;
|
||||
useInvertedBackground: "noInvert" | "invertDefault";
|
||||
cardClassName?: string;
|
||||
imageWrapperClassName?: string;
|
||||
imageClassName?: string;
|
||||
iconClassName?: string;
|
||||
nameClassName?: string;
|
||||
handleClassName?: string;
|
||||
testimonialClassName?: string;
|
||||
ratingClassName?: string;
|
||||
contentWrapperClassName?: string;
|
||||
}
|
||||
|
||||
const TestimonialCard = memo(({
|
||||
testimonial,
|
||||
showRating,
|
||||
useInvertedBackground,
|
||||
cardClassName = "",
|
||||
imageWrapperClassName = "",
|
||||
imageClassName = "",
|
||||
iconClassName = "",
|
||||
nameClassName = "",
|
||||
handleClassName = "",
|
||||
testimonialClassName = "",
|
||||
ratingClassName = "",
|
||||
contentWrapperClassName = "",
|
||||
}: TestimonialCardProps) => {
|
||||
const Icon = testimonial.icon || Quote;
|
||||
const theme = useTheme();
|
||||
const shouldUseLightText = shouldUseInvertedText(useInvertedBackground, theme.cardStyle);
|
||||
|
||||
return (
|
||||
<div className={cls("relative h-full card rounded-theme-capped p-6 flex flex-col justify-between", showRating ? "gap-5" : "gap-16", cardClassName)}>
|
||||
<div className={cls("flex flex-col gap-5 items-start", contentWrapperClassName)}>
|
||||
{showRating ? (
|
||||
<div className={cls("relative z-1 flex gap-1", ratingClassName)}>
|
||||
{Array.from({ length: 5 }).map((_, index) => (
|
||||
<Star
|
||||
key={index}
|
||||
className={cls(
|
||||
"h-5 w-auto text-accent",
|
||||
index < testimonial.rating ? "fill-accent" : "fill-transparent"
|
||||
)}
|
||||
strokeWidth={1.5}
|
||||
/>
|
||||
))}
|
||||
</div>
|
||||
) : (
|
||||
<Quote className="h-6 w-auto text-accent fill-accent" strokeWidth={1.5} />
|
||||
)}
|
||||
|
||||
<p className={cls("relative z-1 text-lg leading-[1.2]", shouldUseLightText ? "text-background" : "text-foreground", testimonialClassName)}>
|
||||
{testimonial.testimonial}
|
||||
</p>
|
||||
</div>
|
||||
|
||||
<TestimonialAuthor
|
||||
name={testimonial.name}
|
||||
subtitle={testimonial.handle}
|
||||
imageSrc={testimonial.imageSrc}
|
||||
imageAlt={testimonial.imageAlt}
|
||||
icon={Icon}
|
||||
useInvertedBackground={useInvertedBackground}
|
||||
imageWrapperClassName={imageWrapperClassName}
|
||||
imageClassName={imageClassName}
|
||||
iconClassName={iconClassName}
|
||||
nameClassName={nameClassName}
|
||||
subtitleClassName={handleClassName}
|
||||
/>
|
||||
</div>
|
||||
);
|
||||
});
|
||||
|
||||
TestimonialCard.displayName = "TestimonialCard";
|
||||
|
||||
const TestimonialCardThirteen = ({
|
||||
testimonials,
|
||||
showRating,
|
||||
carouselMode = "buttons",
|
||||
uniformGridCustomHeightClasses = "min-h-none",
|
||||
animationType,
|
||||
title,
|
||||
titleSegments,
|
||||
description,
|
||||
tag,
|
||||
tagIcon,
|
||||
buttons,
|
||||
textboxLayout,
|
||||
useInvertedBackground,
|
||||
ariaLabel = "Testimonials section",
|
||||
className = "",
|
||||
containerClassName = "",
|
||||
cardClassName = "",
|
||||
textBoxTitleClassName = "",
|
||||
textBoxTitleImageWrapperClassName = "",
|
||||
textBoxTitleImageClassName = "",
|
||||
textBoxDescriptionClassName = "",
|
||||
imageWrapperClassName = "",
|
||||
imageClassName = "",
|
||||
iconClassName = "",
|
||||
nameClassName = "",
|
||||
handleClassName = "",
|
||||
testimonialClassName = "",
|
||||
ratingClassName = "",
|
||||
contentWrapperClassName = "",
|
||||
gridClassName = "",
|
||||
carouselClassName = "",
|
||||
controlsClassName = "",
|
||||
textBoxClassName = "",
|
||||
textBoxTagClassName = "",
|
||||
textBoxButtonContainerClassName = "",
|
||||
textBoxButtonClassName = "",
|
||||
textBoxButtonTextClassName = "",
|
||||
}: TestimonialCardThirteenProps) => {
|
||||
return (
|
||||
<CardStack
|
||||
mode={carouselMode}
|
||||
gridVariant="uniform-all-items-equal"
|
||||
uniformGridCustomHeightClasses={uniformGridCustomHeightClasses}
|
||||
animationType={animationType}
|
||||
supports3DAnimation={true}
|
||||
|
||||
title={title}
|
||||
titleSegments={titleSegments}
|
||||
description={description}
|
||||
tag={tag}
|
||||
tagIcon={tagIcon}
|
||||
buttons={buttons}
|
||||
textboxLayout={textboxLayout}
|
||||
useInvertedBackground={useInvertedBackground}
|
||||
className={className}
|
||||
containerClassName={containerClassName}
|
||||
gridClassName={gridClassName}
|
||||
carouselClassName={carouselClassName}
|
||||
controlsClassName={controlsClassName}
|
||||
textBoxClassName={textBoxClassName}
|
||||
titleClassName={textBoxTitleClassName}
|
||||
titleImageWrapperClassName={textBoxTitleImageWrapperClassName}
|
||||
titleImageClassName={textBoxTitleImageClassName}
|
||||
descriptionClassName={textBoxDescriptionClassName}
|
||||
tagClassName={textBoxTagClassName}
|
||||
buttonContainerClassName={textBoxButtonContainerClassName}
|
||||
buttonClassName={textBoxButtonClassName}
|
||||
buttonTextClassName={textBoxButtonTextClassName}
|
||||
ariaLabel={ariaLabel}
|
||||
>
|
||||
{testimonials.map((testimonial, index) => (
|
||||
<TestimonialCard
|
||||
key={`${testimonial.id}-${index}`}
|
||||
testimonial={testimonial}
|
||||
showRating={showRating}
|
||||
useInvertedBackground={useInvertedBackground}
|
||||
cardClassName={cardClassName}
|
||||
imageWrapperClassName={imageWrapperClassName}
|
||||
imageClassName={imageClassName}
|
||||
iconClassName={iconClassName}
|
||||
nameClassName={nameClassName}
|
||||
handleClassName={handleClassName}
|
||||
testimonialClassName={testimonialClassName}
|
||||
ratingClassName={ratingClassName}
|
||||
contentWrapperClassName={contentWrapperClassName}
|
||||
/>
|
||||
))}
|
||||
</CardStack>
|
||||
);
|
||||
};
|
||||
|
||||
TestimonialCardThirteen.displayName = "TestimonialCardThirteen";
|
||||
|
||||
export default TestimonialCardThirteen;
|
||||
101
src/components/sections/testimonial/TestimonialCardTwelve.tsx
Normal file
101
src/components/sections/testimonial/TestimonialCardTwelve.tsx
Normal file
@@ -0,0 +1,101 @@
|
||||
"use client";
|
||||
|
||||
import { useState, useEffect } from "react";
|
||||
import AvatarGroup from "@/components/shared/AvatarGroup";
|
||||
import Tag from "@/components/shared/Tag";
|
||||
import { cls, shouldUseInvertedText } from "@/lib/utils";
|
||||
import { useTheme } from "@/providers/themeProvider/ThemeProvider";
|
||||
import type { LucideIcon } from "lucide-react";
|
||||
import type { InvertedBackground } from "@/providers/themeProvider/config/constants";
|
||||
|
||||
type Testimonial = {
|
||||
id: string;
|
||||
name: string;
|
||||
imageSrc: string;
|
||||
imageAlt?: string;
|
||||
};
|
||||
|
||||
interface TestimonialCardTwelveProps {
|
||||
testimonials: Testimonial[];
|
||||
cardTitle: string;
|
||||
cardTag: string;
|
||||
cardTagIcon?: LucideIcon;
|
||||
useInvertedBackground: InvertedBackground;
|
||||
ariaLabel?: string;
|
||||
className?: string;
|
||||
containerClassName?: string;
|
||||
cardClassName?: string;
|
||||
avatarGroupClassName?: string;
|
||||
avatarClassName?: string;
|
||||
cardTitleClassName?: string;
|
||||
cardTagClassName?: string;
|
||||
}
|
||||
|
||||
const TestimonialCardTwelve = ({
|
||||
testimonials,
|
||||
cardTitle,
|
||||
cardTag,
|
||||
cardTagIcon,
|
||||
useInvertedBackground,
|
||||
ariaLabel = "Testimonials section",
|
||||
className = "",
|
||||
containerClassName = "",
|
||||
cardClassName = "",
|
||||
avatarGroupClassName = "",
|
||||
avatarClassName = "",
|
||||
cardTitleClassName = "",
|
||||
cardTagClassName = "",
|
||||
}: TestimonialCardTwelveProps) => {
|
||||
const theme = useTheme();
|
||||
const shouldUseLightText = shouldUseInvertedText(useInvertedBackground, theme.cardStyle);
|
||||
|
||||
const [isMobile, setIsMobile] = useState(false);
|
||||
|
||||
useEffect(() => {
|
||||
const checkMobile = () => setIsMobile(window.innerWidth < 768);
|
||||
checkMobile();
|
||||
window.addEventListener("resize", checkMobile);
|
||||
return () => window.removeEventListener("resize", checkMobile);
|
||||
}, []);
|
||||
|
||||
const avatars = testimonials.map((testimonial) => ({
|
||||
src: testimonial.imageSrc,
|
||||
alt: testimonial.imageAlt || testimonial.name,
|
||||
}));
|
||||
|
||||
return (
|
||||
<section
|
||||
aria-label={ariaLabel}
|
||||
className={cls("relative py-20 w-full", useInvertedBackground === "invertDefault" && "bg-foreground", className)}
|
||||
>
|
||||
<div className={cls("w-content-width mx-auto", containerClassName)}>
|
||||
<div className={cls("w-full card rounded-theme-capped p-8 flex flex-col items-center justify-between gap-8", cardClassName)}>
|
||||
<div className="flex flex-col gap-3 items-center">
|
||||
<Tag
|
||||
text={cardTag}
|
||||
icon={cardTagIcon}
|
||||
useInvertedBackground={useInvertedBackground}
|
||||
className={cardTagClassName}
|
||||
/>
|
||||
<h3 className={cls("relative md:max-w-7/10 text-3xl md:text-5xl font-medium leading-tight text-center text-balance", shouldUseLightText ? "text-background" : "text-foreground", cardTitleClassName)}>
|
||||
{cardTitle}
|
||||
</h3>
|
||||
</div>
|
||||
<AvatarGroup
|
||||
avatars={avatars}
|
||||
className={avatarGroupClassName}
|
||||
avatarClassName={avatarClassName}
|
||||
maxVisible={isMobile ? 3 : 4}
|
||||
ariaLabel="Customer testimonials"
|
||||
avatarImageClassName="h-[var(--width-17_5)] md:h-[var(--width-12_5)]"
|
||||
avatarOverlapClassName="-ml-8"
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
</section>
|
||||
);
|
||||
};
|
||||
|
||||
TestimonialCardTwelve.displayName = "TestimonialCardTwelve";
|
||||
|
||||
export default TestimonialCardTwelve;
|
||||
210
src/components/sections/testimonial/TestimonialCardTwo.tsx
Normal file
210
src/components/sections/testimonial/TestimonialCardTwo.tsx
Normal file
@@ -0,0 +1,210 @@
|
||||
"use client";
|
||||
|
||||
import { memo } from "react";
|
||||
import Image from "next/image";
|
||||
import CardStack from "@/components/cardStack/CardStack";
|
||||
import { cls, shouldUseInvertedText } from "@/lib/utils";
|
||||
import { useTheme } from "@/providers/themeProvider/ThemeProvider";
|
||||
import { Quote } from "lucide-react";
|
||||
import type { LucideIcon } from "lucide-react";
|
||||
import type { ButtonConfig, CardAnimationTypeWith3D, TitleSegment, TextboxLayout, InvertedBackground } from "@/components/cardStack/types";
|
||||
|
||||
type Testimonial = {
|
||||
id: string;
|
||||
name: string;
|
||||
role: string;
|
||||
testimonial: string;
|
||||
imageSrc?: string;
|
||||
imageAlt?: string;
|
||||
icon?: LucideIcon;
|
||||
};
|
||||
|
||||
interface TestimonialCardTwoProps {
|
||||
testimonials: Testimonial[];
|
||||
carouselMode?: "auto" | "buttons";
|
||||
uniformGridCustomHeightClasses?: string;
|
||||
animationType: CardAnimationTypeWith3D;
|
||||
title: string;
|
||||
titleSegments?: TitleSegment[];
|
||||
description: string;
|
||||
tag?: string;
|
||||
tagIcon?: LucideIcon;
|
||||
buttons?: ButtonConfig[];
|
||||
textboxLayout: TextboxLayout;
|
||||
useInvertedBackground: InvertedBackground;
|
||||
ariaLabel?: string;
|
||||
className?: string;
|
||||
containerClassName?: string;
|
||||
cardClassName?: string;
|
||||
textBoxTitleClassName?: string;
|
||||
textBoxTitleImageWrapperClassName?: string;
|
||||
textBoxTitleImageClassName?: string;
|
||||
textBoxDescriptionClassName?: string;
|
||||
imageWrapperClassName?: string;
|
||||
imageClassName?: string;
|
||||
iconClassName?: string;
|
||||
nameClassName?: string;
|
||||
roleClassName?: string;
|
||||
testimonialClassName?: string;
|
||||
gridClassName?: string;
|
||||
carouselClassName?: string;
|
||||
controlsClassName?: string;
|
||||
textBoxClassName?: string;
|
||||
textBoxTagClassName?: string;
|
||||
textBoxButtonContainerClassName?: string;
|
||||
textBoxButtonClassName?: string;
|
||||
textBoxButtonTextClassName?: string;
|
||||
}
|
||||
|
||||
interface TestimonialCardProps {
|
||||
testimonial: Testimonial;
|
||||
shouldUseLightText: boolean;
|
||||
cardClassName?: string;
|
||||
imageWrapperClassName?: string;
|
||||
imageClassName?: string;
|
||||
iconClassName?: string;
|
||||
nameClassName?: string;
|
||||
roleClassName?: string;
|
||||
testimonialClassName?: string;
|
||||
}
|
||||
|
||||
const TestimonialCard = memo(({
|
||||
testimonial,
|
||||
shouldUseLightText,
|
||||
cardClassName = "",
|
||||
imageWrapperClassName = "",
|
||||
imageClassName = "",
|
||||
iconClassName = "",
|
||||
nameClassName = "",
|
||||
roleClassName = "",
|
||||
testimonialClassName = "",
|
||||
}: TestimonialCardProps) => {
|
||||
const Icon = testimonial.icon || Quote;
|
||||
|
||||
return (
|
||||
<div className={cls("relative h-full card rounded-theme-capped p-6 flex flex-col gap-6", cardClassName)}>
|
||||
<div className={cls("relative z-1 h-30 w-fit aspect-square rounded-theme flex items-center justify-center primary-button overflow-hidden", imageWrapperClassName)}>
|
||||
{testimonial.imageSrc ? (
|
||||
<Image
|
||||
src={testimonial.imageSrc}
|
||||
alt={testimonial.imageAlt || testimonial.name}
|
||||
width={800}
|
||||
height={800}
|
||||
className={cls("absolute inset-0 h-full w-full object-cover", imageClassName)}
|
||||
unoptimized={testimonial.imageSrc.startsWith('http') || testimonial.imageSrc.startsWith('//')}
|
||||
aria-hidden={testimonial.imageAlt === ""}
|
||||
/>
|
||||
) : (
|
||||
<Icon className={cls("h-1/2 w-1/2 text-background", iconClassName)} strokeWidth={1} />
|
||||
)}
|
||||
</div>
|
||||
|
||||
<div className="relative z-1 flex flex-col gap-1 mt-1">
|
||||
<h3 className={cls("text-2xl font-medium leading-[1.1]", shouldUseLightText ? "text-background" : "text-foreground", nameClassName)}>
|
||||
{testimonial.name}
|
||||
</h3>
|
||||
<p className={cls("text-base leading-[1.1]", shouldUseLightText ? "text-background" : "text-foreground", roleClassName)}>
|
||||
{testimonial.role}
|
||||
</p>
|
||||
</div>
|
||||
|
||||
<p className={cls("relative z-1 text-lg leading-[1.25]", shouldUseLightText ? "text-background" : "text-foreground", testimonialClassName)}>
|
||||
{testimonial.testimonial}
|
||||
</p>
|
||||
</div>
|
||||
);
|
||||
});
|
||||
|
||||
TestimonialCard.displayName = "TestimonialCard";
|
||||
|
||||
const TestimonialCardTwo = ({
|
||||
testimonials,
|
||||
carouselMode = "buttons",
|
||||
uniformGridCustomHeightClasses = "min-h-none",
|
||||
animationType,
|
||||
title,
|
||||
titleSegments,
|
||||
description,
|
||||
tag,
|
||||
tagIcon,
|
||||
buttons,
|
||||
textboxLayout,
|
||||
useInvertedBackground,
|
||||
ariaLabel = "Testimonials section",
|
||||
className = "",
|
||||
containerClassName = "",
|
||||
cardClassName = "",
|
||||
textBoxTitleClassName = "",
|
||||
textBoxTitleImageWrapperClassName = "",
|
||||
textBoxTitleImageClassName = "",
|
||||
textBoxDescriptionClassName = "",
|
||||
imageWrapperClassName = "",
|
||||
imageClassName = "",
|
||||
iconClassName = "",
|
||||
nameClassName = "",
|
||||
roleClassName = "",
|
||||
testimonialClassName = "",
|
||||
gridClassName = "",
|
||||
carouselClassName = "",
|
||||
controlsClassName = "",
|
||||
textBoxClassName = "",
|
||||
textBoxTagClassName = "",
|
||||
textBoxButtonContainerClassName = "",
|
||||
textBoxButtonClassName = "",
|
||||
textBoxButtonTextClassName = "",
|
||||
}: TestimonialCardTwoProps) => {
|
||||
const theme = useTheme();
|
||||
const shouldUseLightText = shouldUseInvertedText(useInvertedBackground, theme.cardStyle);
|
||||
return (
|
||||
<CardStack
|
||||
mode={carouselMode}
|
||||
gridVariant="uniform-all-items-equal"
|
||||
uniformGridCustomHeightClasses={uniformGridCustomHeightClasses}
|
||||
animationType={animationType}
|
||||
supports3DAnimation={true}
|
||||
|
||||
title={title}
|
||||
titleSegments={titleSegments}
|
||||
description={description}
|
||||
tag={tag}
|
||||
tagIcon={tagIcon}
|
||||
buttons={buttons}
|
||||
textboxLayout={textboxLayout}
|
||||
useInvertedBackground={useInvertedBackground}
|
||||
className={className}
|
||||
containerClassName={containerClassName}
|
||||
gridClassName={gridClassName}
|
||||
carouselClassName={carouselClassName}
|
||||
controlsClassName={controlsClassName}
|
||||
textBoxClassName={textBoxClassName}
|
||||
titleClassName={textBoxTitleClassName}
|
||||
titleImageWrapperClassName={textBoxTitleImageWrapperClassName}
|
||||
titleImageClassName={textBoxTitleImageClassName}
|
||||
descriptionClassName={textBoxDescriptionClassName}
|
||||
tagClassName={textBoxTagClassName}
|
||||
buttonContainerClassName={textBoxButtonContainerClassName}
|
||||
buttonClassName={textBoxButtonClassName}
|
||||
buttonTextClassName={textBoxButtonTextClassName}
|
||||
ariaLabel={ariaLabel}
|
||||
>
|
||||
{testimonials.map((testimonial, index) => (
|
||||
<TestimonialCard
|
||||
key={`${testimonial.id}-${index}`}
|
||||
testimonial={testimonial}
|
||||
shouldUseLightText={shouldUseLightText}
|
||||
cardClassName={cardClassName}
|
||||
imageWrapperClassName={imageWrapperClassName}
|
||||
imageClassName={imageClassName}
|
||||
iconClassName={iconClassName}
|
||||
nameClassName={nameClassName}
|
||||
roleClassName={roleClassName}
|
||||
testimonialClassName={testimonialClassName}
|
||||
/>
|
||||
))}
|
||||
</CardStack>
|
||||
);
|
||||
};
|
||||
|
||||
TestimonialCardTwo.displayName = "TestimonialCardTwo";
|
||||
|
||||
export default TestimonialCardTwo;
|
||||
Reference in New Issue
Block a user