From a080bc4361f1de2a536c996316a5020c0114801b Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E2=80=9CSalar?= <“salar.sali97@gmail.com”> Date: Tue, 30 Aug 2022 14:05:37 +0300 Subject: [PATCH] Featured articales section - Card component --- src/components/Card.tsx | 66 ++++++---- src/components/FeaturedArticales.tsx | 188 +++++++++++++++++++++++++++ 2 files changed, 229 insertions(+), 25 deletions(-) create mode 100644 src/components/FeaturedArticales.tsx diff --git a/src/components/Card.tsx b/src/components/Card.tsx index 11089f8..49818fc 100644 --- a/src/components/Card.tsx +++ b/src/components/Card.tsx @@ -1,5 +1,5 @@ -import React, { Children } from "react"; import classNames from "classnames"; +import React from "react"; /* -------------------------------------------------------------------------- */ /* Components */ @@ -27,7 +27,7 @@ const Card = ({ children, className }: Props) => { <div className={classNames([ className, - "inline-flex flex-col justify-between p-4 max-w-xs lg:max-w-sm h-52 items-start rounded border border-[#F0F0F0] gap-y-8 overflow-hidden", + "inline-flex flex-col justify-between p-4 items-start rounded border border-gray-75 gap-y-8 overflow-hidden", ])} > {children} @@ -35,69 +35,71 @@ const Card = ({ children, className }: Props) => { ); }; -// Avatar props and function +// Avatar function type AvatarProps = { children?: React.ReactNode; }; Card.Avatar = function CardAvatar({ children }: AvatarProps) { - return <>{children}</>; + return <div>{children}</div>; }; -// The end of Body props and function -// Title props and function +// Title function Card.Title = function CardTitle({ children, className }: Props) { return ( - <Heading className={classNames([className, "select-none leading-7"])}> + <Heading className={classNames([className, "select-none "])}> {children} </Heading> ); }; -// The end Title props and function -// Body props and function +// SubTitle function +Card.SubTitle = function CardSubTitle({ children, className }: Props) { + return ( + <Typography className={classNames([className, ""])}>{children}</Typography> + ); +}; + +// Body function Card.Body = function CardTitle({ children, className }: Props) { return ( - <Typography + <Typography // fontWeightVariant="normal" - className={classNames([className, "text-sm"])} + className={classNames([className, "text-sm h-14 overflow-hidden "])} > {children} </Typography> ); }; -// The end of Body props and function -// Cardheader props and function -Card.Cardheader = function CardCardheader({ children, className }: Props) { +// Cardheader function +Card.CardHeader = function CardCardHeader({ children, className }: Props) { return ( - <div className={classNames([className, "first flex items-start gap-4"])}> + <div className={classNames([className, "flex items-start gap-4"])}> {children} </div> ); }; -// The end of Cardheader props and function -// Cardcontent props and function -Card.Cardcontent = function CardCardcontent({ children, className }: Props) { +// Cardcontent function +Card.CardContent = function CardCardContent({ children, className }: Props) { return ( - <div className={classNames([className, "flex flex-col gap-y-4"])}> + <div className={classNames([className, "flex flex-col gap-y-4 "])}> {children} </div> ); }; -// The end of Cardcontent props and function -// Cardaction props and function -type CardactionProps = { +// Cardaction function +type CardActionProps = { children: React.ReactNode; className?: string | undefined; href?: string; }; -Card.Cardaction = function CardCardaction({ +Card.CardAction = function CardCardAction({ children, className, href = "#", -}: CardactionProps) { +}: CardActionProps) { return ( <Link href={href} @@ -107,6 +109,20 @@ Card.Cardaction = function CardCardaction({ </Link> ); }; -// The end of Cardaction props and function + +// CardMedia function +type CardMediaProps = { + children?: React.ReactNode; + className?: string | undefined; + src?: string; +}; +Card.CardMedia = function CardCardMedia({ + className, + src = "#", +}: CardMediaProps) { + return ( + <img src={src} className={classNames([className, "w-full h-32 rounded"])} /> + ); +}; export default Card; diff --git a/src/components/FeaturedArticales.tsx b/src/components/FeaturedArticales.tsx new file mode 100644 index 0000000..37eaef6 --- /dev/null +++ b/src/components/FeaturedArticales.tsx @@ -0,0 +1,188 @@ +import { useRef } from "react"; + +/* -------------------------------------------------------------------------- */ +/* Skeleton */ +/* -------------------------------------------------------------------------- */ +import "react-loading-skeleton/dist/skeleton.css"; + +/* -------------------------------------------------------------------------- */ +/* Components */ +/* -------------------------------------------------------------------------- */ +import Typography from "./typography/Typography"; +import SkeletonCard from "./SkeletonCard"; +import AspectRatio from "./AspectRatio"; +import Card from "./Card"; +import Link from "./Link"; + +/* -------------------------------------------------------------------------- */ +/* Data */ +/* -------------------------------------------------------------------------- */ +import Articales from "./Articales.json"; + +/* -------------------------------------------------------------------------- */ +/* Icons */ +/* -------------------------------------------------------------------------- */ +import { ReactComponent as SVGArrowRight } from "assets/svg/arrow-right.svg"; +import { ReactComponent as SVGCaretRight } from "assets/svg/caret-right.svg"; +import { ReactComponent as SVGArrowLeft } from "assets/svg/arrow-left.svg"; + +/* -------------------------------------------------------------------------- */ +/* Swiper */ +/* -------------------------------------------------------------------------- */ +import { Swiper, SwiperSlide } from "swiper/react"; +import SwiperCore, { Navigation } from "swiper"; +import { Pagination } from "swiper"; +import "swiper/css/pagination"; +import "swiper/css/navigation"; +import "./styles.css"; +import "swiper/css"; + +/* -------------------------------------------------------------------------- */ +/* How many Skeleton cards should be added to the design */ +/* -------------------------------------------------------------------------- */ +let twoCards: boolean = false; +let threeCards: boolean = false; +console.log(`Number of cards ${Articales.length}`); +if (Articales.length == 2) { + twoCards = true; +} else if (Articales.length == 3) { + threeCards = true; +} + +SwiperCore.use([Navigation]); + +const FeaturedArticales = () => { + const navigationPrevRef = useRef(null); + const navigationNextRef = useRef(null); + const paginationRef = useRef(null); + + return ( + <div className="slider-wrapper Articales"> + <div className="flex justify-end gap-2 my-2"> + <div + className="prev inline-flex justify-center items-center + w-9 h-9 bg-blue-600 rounded cursor-pointer + opacity-0 md:opacity-100" + ref={navigationPrevRef} + > + <SVGArrowLeft className="w-6 h-6 fill-white "></SVGArrowLeft> + </div> + <div + className="next inline-flex justify-center items-center + w-9 h-9 bg-blue-600 rounded cursor-pointer + opacity-0 md:opacity-100" + ref={navigationNextRef} + > + <SVGArrowRight className="w-6 h-6 fill-white "></SVGArrowRight> + </div> + </div> + + <Swiper + slidesPerView={1.25} + slidesPerGroup={1} + loop={Articales.length > 4 ? true : false} + pagination={{ el: ".pagination", clickable: true }} + navigation={{ + prevEl: ".prev", + nextEl: ".next", + }} + breakpoints={{ + 768: { + slidesPerView: 2, + slidesPerGroup: 2, + }, + 1024: { + slidesPerView: 2, + slidesPerGroup: 2, + }, + 1280: { + slidesPerView: 4, + slidesPerGroup: 4, + }, + 1580: { + slidesPerView: 4, + slidesPerGroup: 4, + }, + }} + spaceBetween={25} + loopFillGroupWithBlank={true} + modules={[Pagination, Navigation]} + > + {Articales.map((Articale) => ( + <SwiperSlide> + <Card className="flex-1"> + <Card.CardContent> + <AspectRatio> + <AspectRatio.Content> + <img src={Articale.CoverImg} /> + </AspectRatio.Content> + </AspectRatio> + + <Card.Body className="h-14 overflow-hidden"> + {Articale.Body} + </Card.Body> + </Card.CardContent> + + <Card.CardAction href={Articale.Link}> + <Link> + <Typography className="text-blue-500 font-bold"> + Read More + </Typography> + </Link> + <SVGCaretRight className="fill-blue-500 w-4 h-4" /> + </Card.CardAction> + </Card> + </SwiperSlide> + ))} + + {twoCards && [ + <SwiperSlide className="hidden xl:block"> + <SwiperSlide> + <SkeletonCard className="flex-1"> + <SkeletonCard.Content> + <SkeletonCard.Media /> + <SkeletonCard.Body /> + </SkeletonCard.Content> + <SkeletonCard.Action /> + </SkeletonCard> + </SwiperSlide> + </SwiperSlide>, + + <SwiperSlide className="hidden xl:block"> + <SwiperSlide> + <SkeletonCard className="flex-1"> + <SkeletonCard.Content> + <SkeletonCard.Media /> + <SkeletonCard.Body /> + </SkeletonCard.Content> + <SkeletonCard.Action /> + </SkeletonCard> + </SwiperSlide> + </SwiperSlide>, + ]} + + {threeCards && [ + <SwiperSlide className="hidden xl:block"> + <SwiperSlide> + <SkeletonCard className="flex-1"> + <SkeletonCard.Content> + <SkeletonCard.Media /> + <SkeletonCard.Body /> + </SkeletonCard.Content> + <SkeletonCard.Action /> + </SkeletonCard> + </SwiperSlide> + </SwiperSlide>, + ]} + </Swiper> + + <div + className="pagination my-6 w-full h-2 flex justify-center items-center + opacity-0 md:opacity-100" + ref={paginationRef} + /> + </div> + ); +}; + +export default FeaturedArticales;