108 lines
3.1 KiB
TypeScript
108 lines
3.1 KiB
TypeScript
"use client";
|
|
|
|
import { motion, MotionValue, useScroll, useTransform } from "motion/react";
|
|
import React from "react";
|
|
import { ComponentPropsWithoutRef, FC, ReactNode, useRef } from "react";
|
|
|
|
import { cn } from "@/lib/utils";
|
|
import { useContent } from "@/components/layout/content-provider";
|
|
|
|
const Feature247 = () => {
|
|
const content = useContent();
|
|
const { title, description } = content.pages.product.forushgahSazTala.feature247;
|
|
return (
|
|
<section className="py-32">
|
|
<div className="container mx-auto flex flex-col items-center justify-center px-6">
|
|
<div className="flex max-w-4xl justify-center">
|
|
<TextReveal
|
|
title={title}
|
|
className="items-center justify-center text-center"
|
|
>
|
|
{description}
|
|
</TextReveal>
|
|
</div>
|
|
</div>
|
|
</section>
|
|
);
|
|
};
|
|
|
|
export { Feature247 };
|
|
|
|
// 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-[1rem] py-[5rem]">
|
|
<div ref={targetRef} className="flex flex-col justify-center">
|
|
<span className="text-heading-4 text-center font-medium leading-snug tracking-tight text-foreground">
|
|
{title}
|
|
</span>
|
|
<span
|
|
className={cn(
|
|
"flex flex-wrap justify-center p-5 text-2xl font-semibold leading-relaxed text-black/20 md:p-8 md:text-3xl lg:p-10 lg:text-4xl xl:text-5xl dark:text-white/20",
|
|
className,
|
|
)}
|
|
>
|
|
{words.map((word, i) => {
|
|
const start = i / words.length;
|
|
const end = start + 1 / words.length;
|
|
return (
|
|
<Word key={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: opacity }}
|
|
className="text-black dark:text-white"
|
|
>
|
|
{children}
|
|
</motion.span>
|
|
</span>
|
|
);
|
|
};
|