'use client'; import { forwardRef, HTMLAttributes, useEffect, useRef, useState } from 'react'; import styles from './runes-reveal.module.css'; export interface RunesRevealProps extends HTMLAttributes { /** The runes to display (ancient symbols) */ runes: string[]; /** The translation text to reveal */ translation: string; /** Reveal animation speed: slow, medium, fast */ revealSpeed?: 'slow' | 'medium' | 'fast'; /** Rune style: nordic, elven, draconic */ runeStyle?: 'nordic' | 'elven' | 'draconic'; /** Auto-start animation on mount */ autoStart?: boolean; /** Delay before start (ms) */ startDelay?: number; /** Callback when translation completes */ onComplete?: () => void; } const DEFAULT_RUNES = ['ᚠ', 'ᚢ', 'ᚦ', 'ᚨ', 'ᚱ', 'ᚲ', 'ᚷ', 'ᚹ', 'ᚺ', 'ᚾ', 'ᛁ', 'ᛃ', 'ᛇ', 'ᛈ', 'ᛉ', 'ᛊ', 'ᛏ', 'ᛒ', 'ᛖ', 'ᛗ', 'ᛚ', 'ᛜ', 'ᛞ', 'ᛟ']; export const RunesReveal = forwardRef( ( { runes, translation, revealSpeed = 'medium', runeStyle = 'nordic', autoStart = true, startDelay = 1000, onComplete, className, style, ...props }, ref ) => { const [isRevealing, setIsRevealing] = useState(false); const [revealedIndex, setRevealedIndex] = useState(-1); const [showTranslation, setShowTranslation] = useState(false); const timeoutRef = useRef(); const intervalRef = useRef(); useEffect(() => { if (autoStart && !isRevealing) { const startTimeout = setTimeout(() => { setIsRevealing(true); setRevealedIndex(-1); const speedMap = { slow: 300, medium: 150, fast: 80, }; const interval = setInterval(() => { setRevealedIndex((prev) => { const next = prev + 1; if (next >= runes.length) { clearInterval(interval); setShowTranslation(true); onComplete?.(); return next; } return next; }); }, speedMap[revealSpeed]); intervalRef.current = interval; }, startDelay); timeoutRef.current = startTimeout; } return () => { clearTimeout(timeoutRef.current); clearInterval(intervalRef.current); }; }, [autoStart, runes.length, revealSpeed, startDelay, onComplete, isRevealing]); const triggerReveal = () => { if (!isRevealing) { setIsRevealing(true); setRevealedIndex(-1); const speedMap = { slow: 300, medium: 150, fast: 80, }; const interval = setInterval(() => { setRevealedIndex((prev) => { const next = prev + 1; if (next >= runes.length) { clearInterval(interval); setShowTranslation(true); onComplete?.(); return next; } return next; }); }, speedMap[revealSpeed]); intervalRef.current = interval; } }; return (
{runes.map((rune, index) => ( {rune} ))}
{translation}
); }); RunesReveal.displayName = 'RunesReveal'; export default RunesReveal;