Build & Push Docker Image / build (push) Has been cancelled
Refresh the homepage around new hero/services/roadmap sections, shared design tokens, and updated brand assets. Co-authored-by: Cursor <cursoragent@cursor.com>
169 lines
4.4 KiB
TypeScript
169 lines
4.4 KiB
TypeScript
"use client";
|
|
|
|
import { useMemo, useRef } from "react";
|
|
import AutoScroll from "embla-carousel-auto-scroll";
|
|
import { Icon } from "@iconify/react";
|
|
|
|
import {
|
|
Carousel,
|
|
CarouselContent,
|
|
CarouselItem,
|
|
} from "@/components/ui/carousel";
|
|
import { Container } from "@/components/container";
|
|
import { MotionReveal } from "@/components/motion-reveal";
|
|
import { layout } from "@/lib/layout";
|
|
import { type } from "@/lib/typography";
|
|
import { cn } from "@/lib/utils";
|
|
|
|
const NORMAL_SPEED = 1;
|
|
const HOVER_SPEED = 0.35;
|
|
|
|
interface Logo {
|
|
id: string;
|
|
description: string;
|
|
icon: string;
|
|
className?: string;
|
|
}
|
|
|
|
interface Logos3Props {
|
|
heading?: string;
|
|
logos?: Logo[];
|
|
className?: string;
|
|
}
|
|
|
|
const defaultLogos: Logo[] = [
|
|
{
|
|
id: "logo-1",
|
|
description: "Next.js",
|
|
icon: "logos:nextjs-icon",
|
|
className: "size-8",
|
|
},
|
|
{
|
|
id: "logo-2",
|
|
description: "React",
|
|
icon: "logos:react",
|
|
className: "size-8",
|
|
},
|
|
{
|
|
id: "logo-3",
|
|
description: "TypeScript",
|
|
icon: "logos:typescript-icon",
|
|
className: "size-8",
|
|
},
|
|
{
|
|
id: "logo-4",
|
|
description: "Tailwind CSS",
|
|
icon: "logos:tailwindcss-icon",
|
|
className: "size-8",
|
|
},
|
|
{
|
|
id: "logo-5",
|
|
description: "WordPress",
|
|
icon: "skill-icons:wordpress",
|
|
className: "size-8",
|
|
},
|
|
{
|
|
id: "logo-6",
|
|
description: "Google",
|
|
icon: "logos:google-icon",
|
|
className: "size-8",
|
|
},
|
|
{
|
|
id: "logo-7",
|
|
description: "Figma",
|
|
icon: "logos:figma",
|
|
className: "size-8",
|
|
},
|
|
{
|
|
id: "logo-8",
|
|
description: "Vercel",
|
|
icon: "logos:vercel-icon",
|
|
className: "size-8",
|
|
},
|
|
];
|
|
|
|
export function Logos3({
|
|
heading = "همکاری اخیر ما با برندهای معتبر",
|
|
logos = defaultLogos,
|
|
className,
|
|
}: Logos3Props) {
|
|
const speedRef = useRef(NORMAL_SPEED);
|
|
|
|
// Embla reads `speed` each animation frame via numeric coercion (`valueOf`),
|
|
// so hover can slow the scroll without stopping or re-initializing the plugin.
|
|
const autoScrollPlugin = useMemo(
|
|
() =>
|
|
AutoScroll({
|
|
playOnInit: true,
|
|
speed: {
|
|
valueOf: () => speedRef.current,
|
|
} as unknown as number,
|
|
stopOnInteraction: false,
|
|
stopOnMouseEnter: false,
|
|
startDelay: 0,
|
|
}),
|
|
[]
|
|
);
|
|
|
|
return (
|
|
<section
|
|
className={cn(
|
|
"section-shell bg-section-muted",
|
|
layout.scrollMargin,
|
|
layout.sectionY,
|
|
className
|
|
)}
|
|
>
|
|
<Container className="section-content flex flex-col items-center text-center">
|
|
<MotionReveal>
|
|
<h2 className={cn(type.h2, "text-balance")}>{heading}</h2>
|
|
</MotionReveal>
|
|
</Container>
|
|
<Container className="section-content pt-4 md:pt-14 lg:pt-16">
|
|
<MotionReveal>
|
|
<div
|
|
className={cn(
|
|
"relative flex items-center justify-center overflow-x-clip",
|
|
// Fade logos via mask (no painted overlay) so edges match translucent section surfaces
|
|
"[mask-image:linear-gradient(to_right,transparent,black_12%,black_88%,transparent)]",
|
|
"[-webkit-mask-image:linear-gradient(to_right,transparent,black_12%,black_88%,transparent)]"
|
|
)}
|
|
onMouseEnter={() => {
|
|
speedRef.current = HOVER_SPEED;
|
|
}}
|
|
onMouseLeave={() => {
|
|
speedRef.current = NORMAL_SPEED;
|
|
}}
|
|
>
|
|
<Carousel
|
|
opts={{ loop: true, direction: "rtl" }}
|
|
plugins={[autoScrollPlugin]}
|
|
>
|
|
<CarouselContent className="ml-0">
|
|
{logos.map((logo) => (
|
|
<CarouselItem
|
|
key={logo.id}
|
|
className="flex basis-1/3 justify-center pl-0 md:basis-1/5 lg:basis-1/6"
|
|
>
|
|
<div className="mx-2 flex shrink-0 items-center justify-center sm:mx-8 md:mx-10">
|
|
<Icon
|
|
icon={logo.icon}
|
|
className={cn(
|
|
"shrink-0 opacity-70 transition-opacity duration-200 hover:opacity-100",
|
|
"dark:opacity-80 dark:hover:opacity-100",
|
|
logo.className
|
|
)}
|
|
aria-label={logo.description}
|
|
/>
|
|
</div>
|
|
</CarouselItem>
|
|
))}
|
|
</CarouselContent>
|
|
</Carousel>
|
|
</div>
|
|
</MotionReveal>
|
|
</Container>
|
|
</section>
|
|
);
|
|
}
|