add category section

This commit is contained in:
2026-02-17 21:33:47 +03:30
parent f355fd9bb6
commit 53484a07f8
15 changed files with 220 additions and 37 deletions

View File

@@ -1,34 +1,151 @@
"use client"
import { Card, CardContent } from "@/components/UI/card"
import {Carousel, CarouselContent, CarouselItem, CarouselNext, CarouselPrevious} from "@/components/UI/Carousel";
import {
Carousel,
CarouselContent,
CarouselItem,
CarouselNext,
CarouselPrevious,
} from "@/components/UI/Carousel"
import { categories } from "@/data/mockCategories"
import Image from "next/image"
import {easeOut, motion} from "framer-motion"
type Props = {
title: string
description: string
}
export function CarouselComponent() {
const containerVariants = {
hidden: {},
show: {
transition: {
staggerChildren: 0.08,
},
},
}
const itemVariants = {
hidden: {
opacity: 0,
y: 30,
scale: 0.95,
},
show: {
opacity: 1,
y: 0,
scale: 1,
transition: {
duration: 0.4,
ease: easeOut,
},
},
}
export function CarouselComponent({ title, description }: Props) {
return (
<Carousel
dir={"rtl"}
className="w-full"
opts={{
direction: "rtl",
}}
>
<CarouselContent>
{Array.from({ length: 150 }).map((_, index) => (
<CarouselItem key={index} className="basis-1/3">
<div className="p-1">
<Card dir={"rtl"}>
<CardContent className="flex aspect-square items-center justify-center p-6">
hello
</CardContent>
</Card>
</div>
</CarouselItem>
))}
</CarouselContent>
<CarouselPrevious />
<CarouselNext />
</Carousel>
<div className="relative w-full">
<Carousel dir="rtl" className="w-full" opts={{ direction: "rtl" }}>
{/* 🔥 Animated Header */}
<motion.div
initial={{ opacity: 0, x: 40 }}
animate={{ opacity: 1, x: 0 }}
transition={{ duration: 0.6 }}
className="flex items-center justify-between w-full h-full my-5"
>
<span className="flex items-center justify-center gap-x-2">
<motion.div
initial={{ height: 0 }}
animate={{ height: 64 }}
transition={{ duration: 0.5 }}
className="bg-primary-100 w-4 rounded-2xl rounded-tr-none"
/>
<div>
<p className="text-lg font-semibold">{title}</p>
<p className="text-muted-foreground">{description}</p>
</div>
</span>
</motion.div>
{/* 🔥 Animated Carousel Items */}
<motion.div
variants={containerVariants}
initial="hidden"
animate="show"
>
<CarouselContent>
{categories.map((category) => (
<CarouselItem
key={category.id}
className="
md:basis-1/5
lg:basis-1/7
relative
after:content-['']
after:absolute
after:top-1/2
after:-translate-y-1/2
after:left-2
after:w-px
after:h-[50%]
after:bg-border
last:after:hidden
"
>
<motion.div variants={itemVariants} className="p-1">
<motion.div
whileHover={{
scale: 1.05,
y: -5,
}}
transition={{ type: "spring", stiffness: 300 }}
>
<Card dir="rtl" className="transition-shadow hover:shadow-xl cursor-pointer">
<CardContent className="flex flex-col gap-3 h-30 items-center justify-center p-6">
<motion.div
whileHover={{ rotate: 5 }}
transition={{ type: "spring", stiffness: 200 }}
>
<Image
src={category.image}
alt={category.title}
width={80}
height={80}
className="rounded-full"
/>
</motion.div>
<p className="font-semibold text-text-muted">
{category.title}
</p>
</CardContent>
</Card>
</motion.div>
</motion.div>
</CarouselItem>
))}
</CarouselContent>
</motion.div>
{/* 🔥 Animated Controls */}
<motion.div
initial={{ opacity: 0 }}
animate={{ opacity: 1 }}
transition={{ delay: 0.5 }}
className="absolute top-10 left-16 flex gap-2"
>
<motion.div whileTap={{ scale: 0.85 }}>
<CarouselPrevious />
</motion.div>
<motion.div whileTap={{ scale: 0.85 }}>
<CarouselNext />
</motion.div>
</motion.div>
</Carousel>
</div>
)
}

View File

@@ -1,10 +1,17 @@
import {CarouselComponent} from "./CarouselComponent";
import { CarouselComponent } from "./CarouselComponent"
import { useTranslations } from "next-intl"
const Categories = () => {
const t = useTranslations("Categories")
return (
<>
<CarouselComponent />
</>
<div className="container mx-auto my-16">
<CarouselComponent
title={t("title")}
description={t("description")}
/>
</div>
)
}
export default Categories;
export default Categories

View File

@@ -7,7 +7,7 @@ import useEmblaCarousel, {
import { cn } from "@/lib/utils"
import { Button } from "@/components/UI/button"
import {ChevronIcon} from "@/assets";
import {ArrowLeft, ArrowRight, ChevronIcon} from "@/assets";
type CarouselApi = UseEmblaCarouselType[1]
type UseCarouselParameters = Parameters<typeof useEmblaCarousel>
@@ -138,7 +138,7 @@ function CarouselContent({ className, ...props }: React.ComponentProps<"div">) {
return (
<div
ref={carouselRef}
className="overflow-hidden"
className="overflow-hidden border border-text-primary/10 p-1 rounded-2xl"
data-slot="carousel-content"
>
<div
@@ -185,7 +185,7 @@ function CarouselPrevious({
variant={variant}
size={size}
className={cn(
"rounded-full absolute touch-manipulation",
"rounded-xl p-5 absolute touch-manipulation bg-card border border-primary-300",
orientation === "horizontal"
? "top-1/2 -left-12 -translate-y-1/2"
: "-top-12 left-1/2 -translate-x-1/2 rotate-90",
@@ -195,7 +195,7 @@ function CarouselPrevious({
onClick={scrollPrev}
{...props}
>
<ChevronIcon className="cn-rtl-flip" />
<ArrowLeft className="text-text-primary" />
<span className="sr-only">Previous slide</span>
</Button>
)
@@ -215,7 +215,7 @@ function CarouselNext({
variant={variant}
size={size}
className={cn(
"rounded-full absolute touch-manipulation",
"rounded-xl p-5 absolute touch-manipulation bg-card border-primary-300",
orientation === "horizontal"
? "top-1/2 -right-12 -translate-y-1/2"
: "-bottom-12 left-1/2 -translate-x-1/2 rotate-90",
@@ -225,7 +225,7 @@ function CarouselNext({
onClick={scrollNext}
{...props}
>
<ChevronIcon className="cn-rtl-flip rotate-180" />
<ArrowRight className="text-text-primary size-5" />
<span className="sr-only">Next slide</span>
</Button>
)

View File

@@ -11,7 +11,7 @@ function Card({
<div
data-slot="card"
data-size={size}
className={cn("ring-foreground/10 bg-card text-card-foreground gap-4 overflow-hidden rounded-xl py-4 text-sm ring-1 has-data-[slot=card-footer]:pb-0 has-[>img:first-child]:pt-0 data-[size=sm]:gap-3 data-[size=sm]:py-3 data-[size=sm]:has-data-[slot=card-footer]:pb-0 *:[img:first-child]:rounded-t-xl *:[img:last-child]:rounded-b-xl group/card flex flex-col", className)}
className={cn("ring-foreground/10 text-card-foreground gap-4 overflow-hidden rounded-xl py-4 text-sm has-data-[slot=card-footer]:pb-0 has-[>img:first-child]:pt-0 data-[size=sm]:gap-3 data-[size=sm]:py-3 data-[size=sm]:has-data-[slot=card-footer]:pb-0 *:[img:first-child]:rounded-t-xl *:[img:last-child]:rounded-b-xl group/card flex flex-col", className)}
{...props}
/>
)