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 { clsx } from "clsx";
import type { ClassValue } 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 = {
  animate: { opacity: 1, y: 0 },
  exit: { opacity: 0, y: -10 },
  initial: { opacity: 0, y: 10 },
} 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(() => {
    let longest = "";
    for (const sentence of sentences) {
      if (sentence.length > longest.length) {
        longest = sentence;
      }
    }
    return longest;
  }, [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