@erosnicolau/animated-text
Version:
Advanced React animated text component with comprehensive animation effects
150 lines (149 loc) • 7.76 kB
JavaScript
import { jsx as _jsx } from "react/jsx-runtime";
import { DEFAULT_TIMING } from '../types';
import AnimatedPhrase from './AnimatedPhrase';
import AnimatedWord from './AnimatedWord';
import AnimatedLetter from './AnimatedLetter';
import AnimatedWordTypewriterWord from './AnimatedWordTypewriterWord';
import { sanitizeHtml, tokenizeLetters, tokenizePhrases, tokenizeWords, tokenizeWordTypewriter } from '../utils/textProcessing';
const ContentRenderer = ({ content, animation, allowHtml, phraseStyles, usePhrases, itemSpacing, wordEffect, wordDuration, wordEasing, wordStyles, typewriterEffect, typewriterDuration, typewriterEasing, wordTypewriterEffect, wordTypewriterDuration, wordTypewriterEasing, animatedItems, effectiveReplayKey, performanceModeActive, verticalAlign }) => {
// Helper function to simplify effects for performance mode
const getSimplifiedEffect = (effect) => {
if (Array.isArray(effect)) {
// For arrays, prefer fadeIn if available, otherwise take the first effect
for (const e of effect) {
if (e === 'fadeIn')
return ['fadeIn'];
}
// Return the first effect if no fadeIn found
if (effect.length > 0) {
const firstEffect = effect[0];
if (typeof firstEffect === 'string') {
return [firstEffect];
}
}
return ['fadeIn']; // Fallback
}
if (typeof effect === 'string') {
// Allow fadeIn, fallback to fadeIn for other effects
return effect === 'fadeIn' ? effect : 'fadeIn';
}
// For object effects, always fallback to fadeIn
return 'fadeIn';
};
// Optimize effects when performance mode is active
const optimizedWordEffect = performanceModeActive ? getSimplifiedEffect(wordEffect) : wordEffect;
const optimizedTypewriterEffect = performanceModeActive ? getSimplifiedEffect(typewriterEffect) : typewriterEffect;
const optimizedWordTypewriterEffect = performanceModeActive
? getSimplifiedEffect(wordTypewriterEffect)
: wordTypewriterEffect;
const optimizedWordDuration = performanceModeActive ? Math.min(wordDuration, 300) : wordDuration;
const optimizedTypewriterDuration = performanceModeActive ? Math.min(typewriterDuration, 300) : typewriterDuration;
// Create timing defaults for animations
const timingDefaults = {
startDelay: DEFAULT_TIMING.startDelay,
itemSpacing,
defaultDuration: DEFAULT_TIMING.defaultDuration,
defaultEffect: DEFAULT_TIMING.defaultEffect,
defaultEasing: DEFAULT_TIMING.defaultEasing
};
// Enhanced props - phrase-based styling with animations
if ((phraseStyles || animation === 'phrases') && usePhrases) {
let processedContent = content;
if (allowHtml) {
processedContent = sanitizeHtml(content);
}
const tokens = tokenizePhrases(processedContent);
// Create default phrase styles if none provided but animation is 'phrases'
const basePhraseStyles = phraseStyles ||
(animation === 'phrases'
? tokens
.filter(t => t.type === 'phrase')
.map((_, index) => ({
phrase: index + 1,
effect: ['fadeIn', 'slide-up'],
delayBefore: index * itemSpacing,
duration: 600,
easing: 'ease-out'
}))
: []);
// Optimize phrase styles when performance mode is active
const effectivePhraseStyles = performanceModeActive
? basePhraseStyles.map(style => ({
...style,
effect: getSimplifiedEffect(style.effect || ['fadeIn']),
duration: Math.min(style.duration || 600, 300),
delayBefore: Math.min(style.delayBefore || 0, 100)
}))
: basePhraseStyles;
return tokens.map((token, index) => {
if (token.type === 'phrase' && token.phraseIndex) {
return (_jsx(AnimatedPhrase, { phraseIndex: token.phraseIndex, content: token.content, phraseStyles: effectivePhraseStyles, animatedItems: animatedItems, timingDefaults: timingDefaults, globalVerticalAlign: verticalAlign }, `${index}-${effectiveReplayKey}`));
}
else if (token.type === 'html') {
return _jsx("span", { dangerouslySetInnerHTML: { __html: token.content } }, index);
}
else {
return _jsx("span", { children: token.content }, index);
}
});
}
// Word sequence animation (word-by-word typewriter effect)
if (animation === 'word') {
let processedContent = content;
if (allowHtml) {
processedContent = sanitizeHtml(content);
}
const tokens = tokenizeWords(processedContent);
return tokens.map((token, index) => {
if (token.type === 'word' && token.wordIndex > 0) {
// Find specific styling for this word
const wordStyle = wordStyles?.find(style => style.word === token.wordIndex);
return (_jsx(AnimatedWord, { wordIndex: token.wordIndex, content: token.content, effect: optimizedWordEffect, duration: optimizedWordDuration, easing: wordEasing, animatedItems: animatedItems, className: wordStyle?.className }, `${index}-${effectiveReplayKey}`));
}
else {
return (_jsx("span", { dangerouslySetInnerHTML: allowHtml ? { __html: token.content } : undefined, children: !allowHtml ? token.content : undefined }, index));
}
});
}
// Typewriter animation (letter-by-letter)
if (animation === 'typewriter') {
let processedContent = content;
if (allowHtml) {
processedContent = sanitizeHtml(content);
}
const tokens = tokenizeLetters(processedContent);
return tokens.map((token, index) => {
if (token.type === 'letter' && token.letterIndex > 0) {
return (_jsx(AnimatedLetter, { letterIndex: token.letterIndex, content: token.content, effect: optimizedTypewriterEffect, duration: optimizedTypewriterDuration, easing: typewriterEasing, animatedItems: animatedItems }, `${index}-${effectiveReplayKey}`));
}
else {
return (_jsx("span", { dangerouslySetInnerHTML: allowHtml ? { __html: token.content } : undefined, children: !allowHtml ? token.content : undefined }, index));
}
});
}
// Word-typewriter animation (letter-by-letter with word pauses)
if (animation === 'word-typewriter') {
let processedContent = content;
if (allowHtml) {
processedContent = sanitizeHtml(content);
}
const tokens = tokenizeWordTypewriter(processedContent);
return tokens.map((token, index) => {
if (token.type === 'word') {
return (_jsx(AnimatedWordTypewriterWord, { letters: token.letters, effect: optimizedWordTypewriterEffect, duration: wordTypewriterDuration, easing: wordTypewriterEasing, animatedItems: animatedItems }, `${index}-${effectiveReplayKey}`));
}
else {
// Space token
return _jsx("span", { children: token.content }, index);
}
});
}
// HTML support without styling
if (allowHtml) {
const sanitizedContent = sanitizeHtml(content);
return _jsx("span", { dangerouslySetInnerHTML: { __html: sanitizedContent } });
}
// Plain text fallback
return content;
};
export default ContentRenderer;