File size: 3,305 Bytes
c10f8f8
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
"use client";

import { useState, useEffect } from "react";

interface AnimatedTextProps {
  className?: string;
}

export function AnimatedText({ className = "" }: AnimatedTextProps) {
  const [displayText, setDisplayText] = useState("");
  const [currentSuggestionIndex, setCurrentSuggestionIndex] = useState(0);
  const [isTyping, setIsTyping] = useState(true);
  const [showCursor, setShowCursor] = useState(true);
  const [lastTypedIndex, setLastTypedIndex] = useState(-1);
  const [animationComplete, setAnimationComplete] = useState(false);

  // Randomize suggestions on each component mount
  const [suggestions] = useState(() => {
    const baseSuggestions = [
      "create a stunning portfolio!",
      "build a tic tac toe game!",
      "design a website for my restaurant!",
      "make a sleek landing page!",
      "build an e-commerce store!",
      "create a personal blog!",
      "develop a modern dashboard!",
      "design a company website!",
      "build a todo app!",
      "create an online gallery!",
      "make a contact form!",
      "build a weather app!",
    ];

    // Fisher-Yates shuffle algorithm
    const shuffled = [...baseSuggestions];
    for (let i = shuffled.length - 1; i > 0; i--) {
      const j = Math.floor(Math.random() * (i + 1));
      [shuffled[i], shuffled[j]] = [shuffled[j], shuffled[i]];
    }

    return shuffled;
  });

  useEffect(() => {
    if (animationComplete) return;

    let timeout: NodeJS.Timeout;

    const typeText = () => {
      const currentSuggestion = suggestions[currentSuggestionIndex];

      if (isTyping) {
        if (displayText.length < currentSuggestion.length) {
          setDisplayText(currentSuggestion.slice(0, displayText.length + 1));
          setLastTypedIndex(displayText.length);
          timeout = setTimeout(typeText, 80);
        } else {
          // Finished typing, wait then start erasing
          setLastTypedIndex(-1);
          timeout = setTimeout(() => {
            setIsTyping(false);
          }, 2000);
        }
      }
    };

    timeout = setTimeout(typeText, 100);
    return () => clearTimeout(timeout);
  }, [
    displayText,
    currentSuggestionIndex,
    isTyping,
    suggestions,
    animationComplete,
  ]);

  // Cursor blinking effect
  useEffect(() => {
    if (animationComplete) {
      setShowCursor(false);
      return;
    }

    const cursorInterval = setInterval(() => {
      setShowCursor((prev) => !prev);
    }, 600);

    return () => clearInterval(cursorInterval);
  }, [animationComplete]);

  useEffect(() => {
    if (lastTypedIndex >= 0) {
      const timeout = setTimeout(() => {
        setLastTypedIndex(-1);
      }, 400);

      return () => clearTimeout(timeout);
    }
  }, [lastTypedIndex]);

  return (
    <p className={`font-mono ${className}`}>
      Hey DeepSite,&nbsp;
      {displayText.split("").map((char, index) => (
        <span
          key={`${currentSuggestionIndex}-${index}`}
          className={`transition-colors duration-300 ${
            index === lastTypedIndex ? "text-neutral-100" : ""
          }`}
        >
          {char}
        </span>
      ))}
      <span
        className={`${
          showCursor ? "opacity-100" : "opacity-0"
        } transition-opacity`}
      >
        |
      </span>
    </p>
  );
}