diff --git a/src/components/home/FeaturesSection.tsx b/src/components/home/FeaturesSection.tsx index 5fbd023..fd8efd2 100644 --- a/src/components/home/FeaturesSection.tsx +++ b/src/components/home/FeaturesSection.tsx @@ -1,9 +1,9 @@ "use client"; -import { CheckCircle2 } from "lucide-react"; import { useTranslations } from "next-intl"; import Image from "next/image"; -import { useState } from "react"; +import { useState, useEffect, useRef, useCallback } from "react"; +import { CircleArrowRight } from "lucide-react"; import AnimatedFeature from "./client/AnimatedFeature"; const features = [ @@ -13,19 +13,108 @@ const features = [ items: [ { title: "features.ai.item1", - image: "/features/polish.png" + image: "/features/polish.png", }, { title: "features.ai.item2", - image: "/features/grammar.png" - } - ] - } + image: "/features/grammar.png", + }, + ], + }, ] as const; +const SLIDE_DURATION = 5000; + export default function FeaturesSection() { const t = useTranslations("home"); const [activeFeature, setActiveFeature] = useState(0); + const [progress, setProgress] = useState(0); + const progressIntervalRef = useRef(null); + const timeoutRef = useRef(null); + + useEffect(() => { + return () => { + if (progressIntervalRef.current) { + clearInterval(progressIntervalRef.current); + } + if (timeoutRef.current) { + clearTimeout(timeoutRef.current); + } + }; + }, []); + + const advanceToNextFeature = useCallback(() => { + setActiveFeature((prev) => (prev + 1) % features[0].items.length); + }, []); + + useEffect(() => { + setProgress(0); + + if (progressIntervalRef.current) { + clearInterval(progressIntervalRef.current); + } + + if (timeoutRef.current) { + clearTimeout(timeoutRef.current); + } + + const updateInterval = 50; + const progressStep = (updateInterval / SLIDE_DURATION) * 100; + + progressIntervalRef.current = setInterval(() => { + setProgress((prevProgress) => { + const newProgress = prevProgress + progressStep; + + if (newProgress >= 100) { + if (progressIntervalRef.current) { + clearInterval(progressIntervalRef.current); + } + + timeoutRef.current = setTimeout(() => { + advanceToNextFeature(); + }, 500); + + return 100; + } + + return newProgress; + }); + }, updateInterval); + + timeoutRef.current = setTimeout(() => { + advanceToNextFeature(); + }, SLIDE_DURATION + 1000); + + return () => { + if (progressIntervalRef.current) { + clearInterval(progressIntervalRef.current); + } + if (timeoutRef.current) { + clearTimeout(timeoutRef.current); + } + }; + }, [activeFeature, advanceToNextFeature]); + + const handleSlideChange = (index: number) => { + if (index === activeFeature) return; + + if (progressIntervalRef.current) { + clearInterval(progressIntervalRef.current); + } + + if (timeoutRef.current) { + clearTimeout(timeoutRef.current); + } + + setActiveFeature(index); + }; + + const calculateCircleProgress = (percent: number) => { + const radius = 10; + const circumference = 2 * Math.PI * radius; + const offset = circumference - (percent / 100) * circumference; + return { circumference, offset }; + }; return (
@@ -48,47 +137,112 @@ export default function FeaturesSection() { {t(features[0].title)}
    - {features[0].items.map((item, index) => ( -
  • setActiveFeature(index)} - className={`flex items-center gap-3 cursor-pointer relative p-2 rounded-lg transition-all ${ - activeFeature === index ? "bg-primary/10" : "" - }`} - > - - {String(index + 1).padStart(2, "0")} - - { + const { circumference, offset } = + activeFeature === index + ? calculateCircleProgress(progress) + : activeFeature > index || + (activeFeature === 0 && + index === features[0].items.length - 1) + ? calculateCircleProgress(100) + : calculateCircleProgress(0); + + return ( +
  • handleSlideChange(index)} + className={`cursor-pointer relative p-2 rounded-lg transition-all`} > - {t(item.title)} - -
  • - ))} +
    +
    + {activeFeature === index ? ( + + + + + ) : ( + + )} +
    + + {t(item.title)} + +
    + + ); + })}
- -
+ +
+
+ {t(features[0].items[activeFeature].title)}
-
+ +
+
+
+ +
); }