Files
Emil_Shanayev_Resume/components/TypingText.tsx
T
emil28092005andClaude Sonnet 4.6 727996503e feat: rewrite site as Next.js 14 portfolio with terminal aesthetic
Replaces Hugo site with a Next.js App Router (TypeScript) build.
Tailwind CSS custom terminal palette, Framer Motion scroll reveals,
JetBrains Mono font, scanline/vignette overlays, typing animation.
Sections: Hero, Skills, Projects, Contact. Content driven from data/resume.ts.
Deploy workflow updated for static export to gh-pages.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-04-30 04:15:07 +03:00

64 lines
1.5 KiB
TypeScript

'use client'
import { useEffect, useState } from 'react'
interface Props {
phrases: readonly string[]
speed?: number
deleteSpeed?: number
pauseDuration?: number
}
export function TypingText({
phrases,
speed = 75,
deleteSpeed = 38,
pauseDuration = 2200,
}: Props) {
const [displayed, setDisplayed] = useState('')
const [phraseIdx, setPhraseIdx] = useState(0)
const [charIdx, setCharIdx] = useState(0)
const [deleting, setDeleting] = useState(false)
const [pausing, setPausing] = useState(false)
useEffect(() => {
if (pausing) return
const current = phrases[phraseIdx]
if (!deleting) {
if (charIdx < current.length) {
const t = setTimeout(() => setCharIdx((i) => i + 1), speed)
return () => clearTimeout(t)
}
if (phrases.length > 1) {
const t = setTimeout(() => {
setPausing(false)
setDeleting(true)
}, pauseDuration)
setPausing(true)
return () => clearTimeout(t)
}
return
}
if (charIdx > 0) {
const t = setTimeout(() => setCharIdx((i) => i - 1), deleteSpeed)
return () => clearTimeout(t)
}
setDeleting(false)
setPhraseIdx((i) => (i + 1) % phrases.length)
}, [charIdx, deleting, pausing, phraseIdx, phrases, speed, deleteSpeed, pauseDuration])
useEffect(() => {
setDisplayed(phrases[phraseIdx].slice(0, charIdx))
}, [charIdx, phraseIdx, phrases])
return (
<span>
{displayed}
<span className="cursor-blink text-terminal-amber">█</span>
</span>
)
}