add category section
@@ -16,6 +16,10 @@
|
||||
"main_title_line2": "سریع و مطمئن",
|
||||
"contact_us": "تماس با ما"
|
||||
},
|
||||
"Categories" : {
|
||||
"title" : "دسته بندی ها",
|
||||
"description" : "از بین صدها مرکز زیبایی، دقیقا همونی که میخای رو پیدا کن."
|
||||
},
|
||||
"Application": {
|
||||
"title": "دانلود نرم افزار تلفن همراه 141",
|
||||
"description": "نسخهی تلفن همراه اپلیکیشن مرکز مدیریت راههای کشور با قابلیتهایی نظیر نمایش تصاویر دوربینهای، نظارتی در سراسر کشور، مسیریابی همراه با نمایش مسافت و زمان تقریبی و جزئیات مسیر، مشاهده اخبار متنی و شنیداری، کروکی ترافیک استانهای کشور، دریافت اطلاعیههای مربوط به مسیرهای پرتردد و ... آماده دریافت از مارکتهای مختلف میباشد.",
|
||||
|
||||
3
public/icons/arrow-left.svg
Normal file
@@ -0,0 +1,3 @@
|
||||
<svg width="15" height="12" viewBox="0 0 15 12" fill="none" xmlns="http://www.w3.org/2000/svg">
|
||||
<path fill-rule="evenodd" clip-rule="evenodd" d="M6.06694 0.183058C6.31102 0.427136 6.31102 0.822864 6.06694 1.06694L2.13388 5H13.9583C14.3035 5 14.5833 5.27982 14.5833 5.625C14.5833 5.97018 14.3035 6.25 13.9583 6.25H2.13388L6.06694 10.1831C6.31102 10.4271 6.31102 10.8229 6.06694 11.0669C5.82286 11.311 5.42714 11.311 5.18306 11.0669L0.183058 6.06694C-0.0610194 5.82286 -0.0610194 5.42714 0.183058 5.18306L5.18306 0.183058C5.42714 -0.0610194 5.82286 -0.0610194 6.06694 0.183058Z" fill="currentColor"/>
|
||||
</svg>
|
||||
|
After Width: | Height: | Size: 610 B |
3
public/icons/arrow-right.svg
Normal file
@@ -0,0 +1,3 @@
|
||||
<svg width="20" height="20" viewBox="0 0 20 20" fill="none" xmlns="http://www.w3.org/2000/svg">
|
||||
<path fill-rule="evenodd" clip-rule="evenodd" d="M11.2254 4.55806C11.4695 4.31398 11.8652 4.31398 12.1093 4.55806L17.1093 9.55806C17.3533 9.80214 17.3533 10.1979 17.1093 10.4419L12.1093 15.4419C11.8652 15.686 11.4695 15.686 11.2254 15.4419C10.9813 15.1979 10.9813 14.8021 11.2254 14.5581L15.1584 10.625H3.33398C2.98881 10.625 2.70898 10.3452 2.70898 10C2.70898 9.65482 2.98881 9.375 3.33398 9.375H15.1584L11.2254 5.44194C10.9813 5.19786 10.9813 4.80214 11.2254 4.55806Z" fill="currentColor"/>
|
||||
</svg>
|
||||
|
After Width: | Height: | Size: 600 B |
BIN
public/images/categories/فیشیال.jpg
Normal file
|
After Width: | Height: | Size: 6.1 KiB |
BIN
public/images/categories/مو.jpg
Normal file
|
After Width: | Height: | Size: 7.6 KiB |
BIN
public/images/categories/مژه.jpg
Normal file
|
After Width: | Height: | Size: 8.7 KiB |
BIN
public/images/categories/میکاپ.jpg
Normal file
|
After Width: | Height: | Size: 11 KiB |
BIN
public/images/categories/ناخن.jpg
Normal file
|
After Width: | Height: | Size: 5.8 KiB |
BIN
public/images/categories/ژل.jpg
Normal file
|
After Width: | Height: | Size: 9.3 KiB |
@@ -17,3 +17,5 @@ export { default as CalenderIcon } from "&/icons/calender.svg";
|
||||
export { default as LocationIcon } from "&/icons/location.svg";
|
||||
export { default as MarkerIcon } from "&/icons/marker-icon.svg";
|
||||
export { default as MarkerPreviewIcon } from "&/icons/marker-preview.svg";
|
||||
export { default as ArrowRight } from "&/icons/arrow-right.svg";
|
||||
export { default as ArrowLeft } from "&/icons/arrow-left.svg";
|
||||
|
||||
@@ -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>
|
||||
)
|
||||
}
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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>
|
||||
)
|
||||
|
||||
@@ -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}
|
||||
/>
|
||||
)
|
||||
|
||||
47
src/data/mockCategories.ts
Normal file
@@ -0,0 +1,47 @@
|
||||
export const categories = [
|
||||
{
|
||||
id: 1,
|
||||
title: "خدمات مو",
|
||||
image: "/images/categories/مو.jpg",
|
||||
},
|
||||
{
|
||||
id: 2,
|
||||
title: "خدمات میکاپ",
|
||||
image: "/images/categories/میکاپ.jpg",
|
||||
},
|
||||
{
|
||||
id: 3,
|
||||
title: "خدمات ناخن",
|
||||
image: "/images/categories/ناخن.jpg",
|
||||
},
|
||||
{
|
||||
id: 4,
|
||||
title: "خدمات فیشیال",
|
||||
image: "/images/categories/فیشیال.jpg",
|
||||
},
|
||||
{
|
||||
id: 5,
|
||||
title: "خدمات مژه و ابرو",
|
||||
image: "/images/categories/مژه.jpg",
|
||||
},
|
||||
{
|
||||
id: 7,
|
||||
title: "ژل و بوتاکس",
|
||||
image: "/images/categories/ژل.jpg",
|
||||
},
|
||||
{
|
||||
id: 8,
|
||||
title: "ژل و بوتاکس",
|
||||
image: "/images/categories/ژل.jpg",
|
||||
},
|
||||
{
|
||||
id: 9,
|
||||
title: "ژل و بوتاکس",
|
||||
image: "/images/categories/ژل.jpg",
|
||||
},
|
||||
{
|
||||
id: 10,
|
||||
title: "ژل و بوتاکس",
|
||||
image: "/images/categories/ژل.jpg",
|
||||
},
|
||||
]
|
||||