aller au contenu principal

Imagine, code, crée, inspire.

1. Installation avec le CLI de shadcn/ui

Copiez la commande ci-dessous dans votre terminal afin d'installer le composant

pnpm dlx shadcn@latest add @envindavsorg/flip-sentences

2. Installation manuelle

(2.1) installez les dépendances :

pnpm add motion clsx tailwind-merge

(2.2) ajoutez le helper cn :

TypeScriptlib/utils.ts
import { type ClassValue, clsx } from 'clsx';
import { twMerge } from 'tailwind-merge';
 
export const cn = (...inputClasses: ClassValue[]): string => twMerge(clsx(inputClasses));

(2.3) créez le tableau avec les phrases que vous souhaitez voir défiler :

const EXAMPLE_SENTENCES = [
	'Imagine, code, crée, inspire.',
	'Chaque petit pixel compte !',
	'Chaque petit détail compte !',
	'Du concept au déploiement !',
];

(2.4) ajoutez le code suivant :

Reactcomponents/FlipSentences.tsx
'use client';
 
import { AnimatePresence, motion } from 'motion/react';
import { useEffect, useMemo, useState } from 'react';
import { cn } from '@/lib/utils';
 
interface FlipSentencesProps {
	className?: string;
	sentences: string[];
	interval?: number;
	disableAnimation?: boolean;
}
 
const sentenceVariants = {
	initial: { y: 10, opacity: 0 },
	animate: { y: 0, opacity: 1 },
	exit: { y: -10, opacity: 0 },
} as const;
 
const sentenceTransition = {
	duration: 0.25,
	ease: 'easeOut',
} as const;
 
export const FlipSentences = ({
	className,
	sentences,
	interval = 4000,
	disableAnimation = false,
}: FlipSentencesProps) => {
	const [currentIndex, setCurrentIndex] = useState(0);
	const sentenceCount = sentences.length;
 
	const longestSentence = useMemo(() => sentences.reduce((a, b) => (b.length > a.length ? b : a), ''), [sentences]);
 
	useEffect(() => {
		if (disableAnimation) {
			setCurrentIndex(0);
			return;
		}
 
		const advance = () => setCurrentIndex((prev) => (prev + 1) % sentenceCount);
 
		let timer = setInterval(advance, interval);
 
		const handleVisibility = () => {
			clearInterval(timer);
			if (!document.hidden) {
				advance();
				timer = setInterval(advance, interval);
			}
		};
 
		document.addEventListener('visibilitychange', handleVisibility);
 
		return () => {
			clearInterval(timer);
			document.removeEventListener('visibilitychange', handleVisibility);
		};
	}, [sentenceCount, interval, disableAnimation]);
 
	if (disableAnimation) {
		return <p className={cn('font-medium text-theme text-xs sm:text-sm', className)}>{sentences[0]}</p>;
	}
 
	return (
		<div className={cn('relative overflow-hidden', className)}>
			<AnimatePresence mode="wait">
				<motion.p
					animate="animate"
					className="font-medium text-theme text-xs sm:text-sm"
					exit="exit"
					initial="initial"
					key={currentIndex}
					transition={sentenceTransition}
					variants={sentenceVariants}
				>
					{sentences[currentIndex]}
				</motion.p>
			</AnimatePresence>
 
			<span aria-hidden="true" className="invisible block h-0">
				{longestSentence}
			</span>
		</div>
	);
};

(2.5) petite astuce :

Mettez à jour les chemins d'import de vos dépendances pour correspondre à la structure de votre projet et ainsi le faire fonctionner correctement.

3. Utilisation

'use client';
 
import { FlipSentences } from "@/components/flip-sentences";
 
<FlipSentences sentences={EXAMPLE_SENTENCES} />

4. Références