Initial commit
This commit is contained in:
64
src/templates/PricingPage/Community/Carousel.tsx
Normal file
64
src/templates/PricingPage/Community/Carousel.tsx
Normal file
@@ -0,0 +1,64 @@
|
||||
import { useRef, useState } from "react";
|
||||
import { Splide, SplideTrack, SplideSlide } from "@splidejs/react-splide";
|
||||
import Comment from "./Comment";
|
||||
|
||||
type CarouselProps = {
|
||||
items: any;
|
||||
};
|
||||
|
||||
const Carousel = ({ items }: CarouselProps) => {
|
||||
const [activeIndex, setActiveIndex] = useState<number>(0);
|
||||
|
||||
const ref = useRef<any>(null);
|
||||
|
||||
const handleClick = (index: number) => {
|
||||
setActiveIndex(index);
|
||||
ref.current?.go(index);
|
||||
};
|
||||
|
||||
return (
|
||||
<Splide
|
||||
className="splide-visible relative z-2"
|
||||
options={{
|
||||
pagination: false,
|
||||
arrows: false,
|
||||
gap: "1.5rem",
|
||||
}}
|
||||
onMoved={(e, newIndex) => setActiveIndex(newIndex)}
|
||||
hasTrack={false}
|
||||
ref={ref}
|
||||
>
|
||||
<SplideTrack>
|
||||
{items.map((item: any) => (
|
||||
<SplideSlide key={item.id}>
|
||||
<div className="flex h-full">
|
||||
<Comment comment={item} />
|
||||
</div>
|
||||
</SplideSlide>
|
||||
))}
|
||||
</SplideTrack>
|
||||
<div className="flex justify-center mt-8 -mx-2 md:mt-15 lg:hidden">
|
||||
{items.map((item: any, index: number) => (
|
||||
<button
|
||||
className="relative w-6 h-6 mx-2"
|
||||
onClick={() => handleClick(index)}
|
||||
key={item.id}
|
||||
>
|
||||
<span
|
||||
className={`absolute inset-0 bg-conic-gradient rounded-full transition-opacity ${
|
||||
index === activeIndex
|
||||
? "opacity-100"
|
||||
: "opacity-0"
|
||||
}`}
|
||||
></span>
|
||||
<span className="absolute inset-0.25 bg-n-8 rounded-full">
|
||||
<span className="absolute inset-2 bg-n-1 rounded-full"></span>
|
||||
</span>
|
||||
</button>
|
||||
))}
|
||||
</div>
|
||||
</Splide>
|
||||
);
|
||||
};
|
||||
|
||||
export default Carousel;
|
||||
28
src/templates/PricingPage/Community/Comment.tsx
Normal file
28
src/templates/PricingPage/Community/Comment.tsx
Normal file
@@ -0,0 +1,28 @@
|
||||
import Image from "@/components/Image";
|
||||
|
||||
type CommentProps = {
|
||||
comment: any;
|
||||
};
|
||||
|
||||
const Comment = ({ comment }: CommentProps) => (
|
||||
<div className="flex flex-col bg-n-8 border border-n-1/5 rounded-2xl">
|
||||
<div className="quote flex-1 px-5 py-10 md:px-10">{comment.text}</div>
|
||||
<div className="flex items-center px-5 py-6 bg-n-7 rounded-b-[0.9375rem] md:px-10">
|
||||
<div className="mr-5">
|
||||
<h6 className="h6">{comment.name}</h6>
|
||||
<div className="caption text-n-1/25">{comment.role}</div>
|
||||
</div>
|
||||
<div className="ml-auto">
|
||||
<Image
|
||||
className="w-full rounded-full"
|
||||
src={comment.avatarUrl}
|
||||
width={60}
|
||||
height={60}
|
||||
alt={comment.name}
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
|
||||
export default Comment;
|
||||
25
src/templates/PricingPage/Community/Grid.tsx
Normal file
25
src/templates/PricingPage/Community/Grid.tsx
Normal file
@@ -0,0 +1,25 @@
|
||||
import Masonry, { ResponsiveMasonry } from "react-responsive-masonry";
|
||||
import Comment from "./Comment";
|
||||
|
||||
type GridProps = {
|
||||
items: any;
|
||||
};
|
||||
|
||||
const Grid = ({ items }: GridProps) => {
|
||||
return (
|
||||
<ResponsiveMasonry
|
||||
className="relative z-2"
|
||||
columnsCountBreakPoints={{ 768: 2, 1280: 3 }}
|
||||
>
|
||||
<Masonry gutter="1.5rem">
|
||||
{items.map((item: any) => (
|
||||
<div key={item.id}>
|
||||
<Comment comment={item} />
|
||||
</div>
|
||||
))}
|
||||
</Masonry>
|
||||
</ResponsiveMasonry>
|
||||
);
|
||||
};
|
||||
|
||||
export default Grid;
|
||||
50
src/templates/PricingPage/Community/index.tsx
Normal file
50
src/templates/PricingPage/Community/index.tsx
Normal file
@@ -0,0 +1,50 @@
|
||||
import dynamic from "next/dynamic";
|
||||
import { useMediaQuery } from "react-responsive";
|
||||
import Section from "@/components/Section";
|
||||
import Heading from "@/components/Heading";
|
||||
import Image from "@/components/Image";
|
||||
const Grid = dynamic(() => import("./Grid"), { ssr: false });
|
||||
const Carousel = dynamic(() => import("./Carousel"), { ssr: false });
|
||||
|
||||
import { community } from "@/mocks/community";
|
||||
|
||||
type CommunityProps = {};
|
||||
|
||||
const Community = ({}: CommunityProps) => {
|
||||
const isTablet = useMediaQuery({
|
||||
query: "(min-width: 768px)",
|
||||
});
|
||||
|
||||
return (
|
||||
<Section>
|
||||
<div className="container">
|
||||
<Heading
|
||||
className="md:text-center"
|
||||
tagClassName="md:justify-center"
|
||||
tag="ready to get started"
|
||||
title="What the community is saying"
|
||||
/>
|
||||
<div className="relative">
|
||||
{isTablet ? (
|
||||
<Grid items={community} />
|
||||
) : (
|
||||
<Carousel items={community} />
|
||||
)}
|
||||
<div className="absolute top-[18.25rem] -left-[30.375rem] w-[56.625rem] opacity-60 mix-blend-color-dodge pointer-events-none">
|
||||
<div className="absolute top-1/2 left-1/2 w-[58.85rem] h-[58.85rem] -translate-x-3/4 -translate-y-1/2">
|
||||
<Image
|
||||
className="w-full"
|
||||
src="/images/gradient.png"
|
||||
width={942}
|
||||
height={942}
|
||||
alt="Gradient"
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</Section>
|
||||
);
|
||||
};
|
||||
|
||||
export default Community;
|
||||
Reference in New Issue
Block a user