104 lines
3.2 KiB
TypeScript
104 lines
3.2 KiB
TypeScript
"use client";
|
|
|
|
import type { MotionValue } from "motion/react";
|
|
import { motion, useScroll, useTransform } from "motion/react";
|
|
import type { ComponentPropsWithoutRef, FC, ReactNode } from "react";
|
|
import React from "react";
|
|
import { useRef } from "react";
|
|
|
|
import { cn } from "@/lib/utils";
|
|
|
|
const REVEAL_TEXT =
|
|
"تمام تعاملات، سرنخها و قراردادها در یک فضای کاری متمرکز جمع میشود. تیم فروش بدون جابجایی بین ابزارها، سریعتر پیشبینی میکند و معاملات بیشتری میبندد.";
|
|
|
|
const Feature248 = () => {
|
|
return (
|
|
<section dir="rtl" lang="fa" className="dark bg-background py-32">
|
|
<div className="container flex flex-col items-center justify-center">
|
|
<TextReveal className="font-medium xl:text-3xl" maxWidth="86rem">
|
|
{REVEAL_TEXT}
|
|
</TextReveal>
|
|
</div>
|
|
</section>
|
|
);
|
|
};
|
|
|
|
export { Feature248 };
|
|
|
|
// Below is the modified component from Magic UI
|
|
// Original source: https://magicui.design/docs/components/text-reveal
|
|
// Modified to follow our coding standards and design system
|
|
// We respect copyright and attribution to the original creators
|
|
|
|
interface TextRevealProps extends ComponentPropsWithoutRef<"div"> {
|
|
children: string;
|
|
title?: string;
|
|
maxWidth?: string;
|
|
}
|
|
|
|
const TextReveal: FC<TextRevealProps> = ({
|
|
children,
|
|
title,
|
|
className,
|
|
maxWidth,
|
|
}) => {
|
|
const targetRef = useRef<HTMLDivElement | null>(null);
|
|
const { scrollYProgress } = useScroll({
|
|
target: targetRef,
|
|
});
|
|
|
|
const words = children.split(" ");
|
|
|
|
return (
|
|
<div
|
|
ref={targetRef}
|
|
className={cn("relative z-0 h-[200vh]", className)}
|
|
style={{ maxWidth: maxWidth || "56rem" }}
|
|
>
|
|
<div className="sticky top-0 mx-auto flex h-[50%] items-center bg-transparent px-4 py-20">
|
|
<div className="flex flex-col justify-center">
|
|
{title ? (
|
|
<span className="text-center text-lg font-medium tracking-tight text-foreground">
|
|
{title}
|
|
</span>
|
|
) : null}
|
|
<span
|
|
className={cn(
|
|
"flex flex-wrap p-5 text-xl font-semibold text-balance text-black/20 md:p-8 md:text-3xl lg:p-10 lg:text-xl xl:text-2xll dark:text-white/20",
|
|
className,
|
|
)}
|
|
>
|
|
{words.map((word, i) => {
|
|
const start = i / words.length;
|
|
const end = start + 1 / words.length;
|
|
return (
|
|
<Word key={`${word}-${i}`} progress={scrollYProgress} range={[start, end]}>
|
|
{word}
|
|
</Word>
|
|
);
|
|
})}
|
|
</span>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
);
|
|
};
|
|
|
|
interface WordProps {
|
|
children: ReactNode;
|
|
progress: MotionValue<number>;
|
|
range: [number, number];
|
|
}
|
|
|
|
const Word: FC<WordProps> = ({ children, progress, range }) => {
|
|
const opacity = useTransform(progress, range, [0, 1]);
|
|
return (
|
|
<span className="relative mx-1 lg:mx-1.5">
|
|
<span className="absolute opacity-30">{children}</span>
|
|
<motion.span style={{ opacity }} className="text-black dark:text-white">
|
|
{children}
|
|
</motion.span>
|
|
</span>
|
|
);
|
|
};
|