158 lines
4.7 KiB
TypeScript
158 lines
4.7 KiB
TypeScript
"use client";
|
|
|
|
import type { MotionValue } from "motion/react";
|
|
import { motion, useScroll, useTransform } from "motion/react";
|
|
import React, { useRef } from "react";
|
|
|
|
import { AuroraText } from "@/components/magicui/aurora-text";
|
|
import { cn } from "@/lib/utils";
|
|
|
|
interface Feature249Props {
|
|
className?: string;
|
|
}
|
|
|
|
const Feature249 = ({ className }: Feature249Props) => {
|
|
return (
|
|
<section
|
|
dir="rtl"
|
|
lang="fa"
|
|
className={cn("relative w-full min-w-0 overflow-hidden py-32", className)}
|
|
>
|
|
<div className="container min-w-0">
|
|
<ContainerScroll
|
|
titleComponent={
|
|
<>
|
|
<p className="mx-auto mb-4 max-w-3xl text-pretty text-muted-foreground md:text-xl">
|
|
با یادآورهای هوشمند و اولویتبندی سرنخ، زمان تیم روی معاملات با احتمال بیشتر صرف میشود و چرخه فروش کوتاهتر میشود.
|
|
</p>
|
|
<h1 className="text-3xl font-semibold tracking-tighter text-balance break-words sm:text-4xl md:text-5xl lg:text-6xl xl:text-7xl">
|
|
<span className="pe-2 text-muted-foreground/40 sm:pe-4 md:pe-6">
|
|
پیشبینی درآمد را دقیقتر کنید
|
|
</span>
|
|
<AuroraText
|
|
colors={["#ff6449", "#ff6449", "#6248fe", "#6248fe"]}
|
|
>
|
|
چرخه فروش را از سرنخ تا قرارداد در یک مسیر مشخص پیش ببرید
|
|
</AuroraText>
|
|
</h1>
|
|
</>
|
|
}
|
|
>
|
|
<img
|
|
src="/block-media/shadcnblocks/block/dashboard/admin-dashboard-1.png"
|
|
alt="صف اولویت سرنخها"
|
|
height={720}
|
|
width={1400}
|
|
className="mx-auto size-full rounded-2xl object-cover object-start-top"
|
|
draggable={false}
|
|
/>
|
|
</ContainerScroll>
|
|
</div>
|
|
</section>
|
|
);
|
|
};
|
|
|
|
export { Feature249 };
|
|
|
|
// Below is the modified component from Aceternity UI
|
|
// Original source: npx shadcn@latest add https://ui.aceternity.com/registry/container-scroll-animation.json
|
|
// Modified to follow our coding standards and design system
|
|
// We respect copyright and attribution to the original creators
|
|
|
|
export const ContainerScroll = ({
|
|
titleComponent,
|
|
children,
|
|
}: {
|
|
titleComponent: string | React.ReactNode;
|
|
children: React.ReactNode;
|
|
}) => {
|
|
const containerRef = useRef<HTMLDivElement>(null);
|
|
const { scrollYProgress } = useScroll({
|
|
target: containerRef,
|
|
});
|
|
const [isMobile, setIsMobile] = React.useState(false);
|
|
|
|
React.useEffect(() => {
|
|
const checkMobile = () => {
|
|
setIsMobile(window.innerWidth <= 768);
|
|
};
|
|
checkMobile();
|
|
window.addEventListener("resize", checkMobile);
|
|
return () => {
|
|
window.removeEventListener("resize", checkMobile);
|
|
};
|
|
}, []);
|
|
|
|
const scaleDimensions = () => {
|
|
// Keep scale ≤ 1 so the 3D card never bleeds past the preview frame.
|
|
return isMobile ? [0.7, 0.9] : [0.95, 1];
|
|
};
|
|
|
|
const rotate = useTransform(scrollYProgress, [0, 1], [20, 0]);
|
|
const scale = useTransform(scrollYProgress, [0, 1], scaleDimensions());
|
|
const translate = useTransform(scrollYProgress, [0, 1], [0, -100]);
|
|
|
|
return (
|
|
<div
|
|
className="relative flex h-[40rem] w-full min-w-0 items-center justify-center overflow-hidden md:h-[50rem] lg:h-[60rem]"
|
|
ref={containerRef}
|
|
>
|
|
<div
|
|
className="relative w-full min-w-0"
|
|
style={{
|
|
perspective: "1000px",
|
|
}}
|
|
>
|
|
<Header translate={translate} titleComponent={titleComponent} />
|
|
<Card rotate={rotate} translate={translate} scale={scale}>
|
|
{children}
|
|
</Card>
|
|
</div>
|
|
</div>
|
|
);
|
|
};
|
|
|
|
export const Header = ({
|
|
translate,
|
|
titleComponent,
|
|
}: {
|
|
translate: MotionValue<number>;
|
|
titleComponent: React.ReactNode;
|
|
}) => {
|
|
return (
|
|
<motion.div
|
|
style={{
|
|
translateY: translate,
|
|
}}
|
|
className="mx-auto w-full min-w-0 max-w-5xl px-2 text-center sm:px-4"
|
|
>
|
|
{titleComponent}
|
|
</motion.div>
|
|
);
|
|
};
|
|
|
|
export const Card = ({
|
|
rotate,
|
|
scale,
|
|
children,
|
|
}: {
|
|
rotate: MotionValue<number>;
|
|
scale: MotionValue<number>;
|
|
translate: MotionValue<number>;
|
|
children: React.ReactNode;
|
|
}) => {
|
|
return (
|
|
<motion.div
|
|
style={{
|
|
rotateX: rotate,
|
|
scale,
|
|
}}
|
|
className="mx-auto -mt-12 h-[24rem] w-full min-w-0 max-w-5xl overflow-hidden rounded-[30px] p-2 shadow-2xl md:h-[36rem] md:p-6 lg:h-[40rem]"
|
|
>
|
|
<div className="size-full min-w-0 overflow-hidden rounded-2xl bg-gray-100 md:rounded-2xl md:p-4 dark:bg-zinc-900">
|
|
{children}
|
|
</div>
|
|
</motion.div>
|
|
);
|
|
};
|