173 lines
5.3 KiB
TypeScript
173 lines
5.3 KiB
TypeScript
"use client";
|
|
|
|
import Autoplay from "embla-carousel-autoplay";
|
|
import { useEffect, useRef, useState } from "react";
|
|
|
|
import type { CarouselApi } from "@/components/ui/carousel";
|
|
import {
|
|
Carousel,
|
|
CarouselContent,
|
|
CarouselItem,
|
|
} from "@/components/ui/carousel";
|
|
import { cn } from "@/lib/utils";
|
|
|
|
const technologies = [
|
|
{
|
|
name: "Next",
|
|
command: "npx create-next-app my-app",
|
|
image:
|
|
"/block-media/shadcnblocks/block/logos/nextjs-icon.svg",
|
|
},
|
|
{
|
|
name: "Vite",
|
|
command: "npm create vite@latest",
|
|
image:
|
|
"/block-media/shadcnblocks/block/logos/vite-icon.svg",
|
|
},
|
|
{
|
|
name: "Remix",
|
|
command: "npx create-remix@latest my-app",
|
|
image:
|
|
"/block-media/shadcnblocks/block/logos/remix-icon.svg",
|
|
},
|
|
{
|
|
name: "Gatsby",
|
|
command: "npm init gatsby",
|
|
image:
|
|
"/block-media/shadcnblocks/block/logos/gatsby-icon.svg",
|
|
},
|
|
{
|
|
name: "Astro",
|
|
command: "npm create astro@latest",
|
|
image:
|
|
"/block-media/shadcnblocks/block/logos/astro-icon.svg",
|
|
},
|
|
|
|
{
|
|
name: "Laravel",
|
|
command: "laravel new my-app ",
|
|
image:
|
|
"/block-media/shadcnblocks/block/logos/laravel-icon.svg",
|
|
},
|
|
{
|
|
name: "React",
|
|
command: "npx create-react-app my-app",
|
|
image:
|
|
"/block-media/shadcnblocks/block/logos/react-icon.svg",
|
|
},
|
|
];
|
|
|
|
interface Hero14Props {
|
|
className?: string;
|
|
}
|
|
|
|
const Hero14 = ({ className }: Hero14Props) => {
|
|
const plugin = useRef(Autoplay({ delay: 4000, stopOnInteraction: false }));
|
|
const [api, setApi] = useState<CarouselApi>();
|
|
const [current, setCurrent] = useState(0);
|
|
const [fadeIn, setFadeIn] = useState(true);
|
|
|
|
useEffect(() => {
|
|
if (!api) return;
|
|
|
|
// Set initial selection
|
|
setCurrent(api.selectedScrollSnap());
|
|
|
|
// Create a reusable update function
|
|
const updateCurrent = () => {
|
|
// Trigger fade out and then update with fade in
|
|
setFadeIn(false);
|
|
setTimeout(() => {
|
|
setCurrent(api.selectedScrollSnap());
|
|
setFadeIn(true);
|
|
}, 200); // Short delay for the fade effect
|
|
};
|
|
|
|
// Add event listeners for both 'select' and 'settle'
|
|
api.on("select", updateCurrent);
|
|
api.on("settle", updateCurrent);
|
|
|
|
// Clean up event listeners when component unmounts
|
|
return () => {
|
|
api.off("select", updateCurrent);
|
|
api.off("settle", updateCurrent);
|
|
};
|
|
}, [api]);
|
|
|
|
// Function to manually select a technology
|
|
const selectTechnology = (index: number) => {
|
|
if (api) {
|
|
api.scrollTo(index);
|
|
}
|
|
};
|
|
|
|
return (
|
|
<section className={cn("py-32", className)}>
|
|
<div className="container">
|
|
<div className="flex flex-col justify-center">
|
|
<h1 className="mx-auto mb-4 max-w-2xl text-center text-xl font-bold md:text-2xll">
|
|
راهاندازی در چند دقیقه
|
|
</h1>
|
|
<p className="mx-auto mt-4 max-w-xl text-center text-lg text-muted-foreground">
|
|
بدون پیچیدگی و تنها با چند مرحله ساده، پروژه خود را راهاندازی کرده و توسعه را آغاز کنید.
|
|
</p>
|
|
<div className="mx-auto mt-8 mb-12 flex h-[60px] w-fit items-center gap-2 rounded-md bg-muted px-4 py-2 text-center">
|
|
<div
|
|
className={cn(
|
|
"flex items-center gap-2 transition-opacity duration-300",
|
|
fadeIn ? "opacity-100" : "opacity-0",
|
|
)}
|
|
>
|
|
<img
|
|
src={technologies[current]?.image}
|
|
alt={technologies[current]?.name}
|
|
className="h-4 md:h-7"
|
|
/>
|
|
|
|
<p className="border-l px-2 font-mono text-sm">
|
|
{technologies[current]?.command}
|
|
</p>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
<Carousel
|
|
setApi={setApi}
|
|
plugins={[plugin.current]}
|
|
opts={{
|
|
loop: true,
|
|
}}
|
|
className="relative mx-auto w-full max-w-3xl before:absolute before:top-0 before:bottom-0 before:left-0 before:z-10 before:w-36 before:bg-linear-to-r before:from-background before:to-transparent after:absolute after:top-0 after:right-0 after:bottom-0 after:z-10 after:w-36 after:bg-linear-to-l after:from-background after:to-transparent"
|
|
onMouseEnter={plugin.current.stop}
|
|
onMouseLeave={plugin.current.reset}
|
|
>
|
|
<CarouselContent>
|
|
{technologies.map((technology, idx) => (
|
|
<CarouselItem
|
|
key={idx}
|
|
className="basis-1/3 select-none sm:basis-1/4 md:basis-1/6"
|
|
>
|
|
<div
|
|
className={cn(
|
|
"flex cursor-pointer items-center justify-center gap-2 rounded-md border p-6",
|
|
idx === current ? "border-input" : "border-transparent",
|
|
)}
|
|
onClick={() => selectTechnology(idx)}
|
|
>
|
|
<img
|
|
className="h-4 shrink-0 md:h-7"
|
|
src={technology.image}
|
|
alt={technology.name}
|
|
/>
|
|
<p>{technology.name}</p>
|
|
</div>
|
|
</CarouselItem>
|
|
))}
|
|
</CarouselContent>
|
|
</Carousel>
|
|
</div>
|
|
</section>
|
|
);
|
|
};
|
|
|
|
export { Hero14 };
|