TLDR Section
Section de resume pour un apercu rapide du contenu. Installez TLDR Section depuis le registre VLLNT UI avec la CLI shadcn.
Section de resume pour un apercu rapide du contenu. Fait partie de la famille content dans VLLNT UI, il est publie comme entree de registre lisible par machine — copiez la source directement dans votre application avec la CLI shadcn et possedez-la, sans dependance d'execution a une bibliotheque de composants.
Apercu
Basculez entre clair et sombre pour inspecter l'apercu Storybook integre.
Installation
Ajoutez TLDR Section a votre projet avec la CLI shadcn. La source arrive dans votre code, prete a etre adaptee :
pnpm dlx shadcn@latest add https://ui.vllnt.ai/r/tldr-section.jsonSource
"use client";
import { useEffect, useReducer, useRef, useState } from "react";
type TLDRSectionProps = {
children: React.ReactNode;
label: string;
};
type SkeletonAction = "hide" | "show";
function skeletonReducer(_state: boolean, action: SkeletonAction): boolean {
return action === "show";
}
export function TLDRSection({ children, label }: TLDRSectionProps) {
const [isExpanded, setIsExpanded] = useState(false);
const [showSkeleton, dispatchSkeleton] = useReducer(skeletonReducer, false);
const hasBeenOpenedRef = useRef(false);
const timerReference = useRef<NodeJS.Timeout | null>(null);
useEffect(() => {
if (isExpanded && !hasBeenOpenedRef.current) {
// Clear any existing timer
if (timerReference.current) {
clearTimeout(timerReference.current);
}
const rafId = requestAnimationFrame(() => {
dispatchSkeleton("show");
hasBeenOpenedRef.current = true;
});
// Simulate loading with skeleton
timerReference.current = setTimeout(() => {
dispatchSkeleton("hide");
timerReference.current = null;
}, 800);
return () => {
cancelAnimationFrame(rafId);
if (timerReference.current) {
clearTimeout(timerReference.current);
timerReference.current = null;
}
dispatchSkeleton("hide");
};
}
return () => {
if (timerReference.current) {
clearTimeout(timerReference.current);
timerReference.current = null;
}
dispatchSkeleton("hide");
};
}, [isExpanded]);
return (
<div className="my-8 rounded-lg border border-border bg-muted/30 overflow-hidden">
<button
className="flex items-center justify-between w-full px-4 py-3 hover:bg-muted/50 transition-colors"
onClick={() => {
setIsExpanded(!isExpanded);
}}
type="button"
>
<div className="flex items-center gap-3">
<svg
className="size-5 text-muted-foreground"
fill="none"
stroke="currentColor"
strokeWidth="2"
viewBox="0 0 24 24"
xmlns="http://www.w3.org/2000/svg"
>
<path
d="M4 6h16M4 12h16M4 18h16"
strokeLinecap="round"
strokeLinejoin="round"
/>
</svg>
<span className="text-sm font-medium text-foreground">{label}</span>
</div>
<svg
className={`size-4 text-muted-foreground transition-transform ${isExpanded ? "rotate-180" : ""}`}
fill="none"
stroke="currentColor"
strokeWidth="2"
viewBox="0 0 24 24"
xmlns="http://www.w3.org/2000/svg"
>
<path
d="M19 9l-7 7-7-7"
strokeLinecap="round"
strokeLinejoin="round"
/>
</svg>
</button>
{isExpanded ? (
<div className="px-4 pb-4 pt-2 border-t border-border">
{showSkeleton ? (
<div className="space-y-3">
<div className="relative h-4 bg-muted/50 rounded overflow-hidden">
<div className="absolute inset-0 bg-gradient-to-r from-transparent via-amber-400/40 to-transparent animate-shimmer" />
</div>
<div className="relative h-4 bg-muted/50 rounded overflow-hidden w-5/6">
<div className="absolute inset-0 bg-gradient-to-r from-transparent via-amber-400/40 to-transparent animate-shimmer" />
</div>
<div className="relative h-4 bg-muted/50 rounded overflow-hidden w-4/5">
<div className="absolute inset-0 bg-gradient-to-r from-transparent via-amber-400/40 to-transparent animate-shimmer" />
</div>
<div className="relative h-4 bg-muted/50 rounded overflow-hidden w-3/4">
<div className="absolute inset-0 bg-gradient-to-r from-transparent via-amber-400/40 to-transparent animate-shimmer" />
</div>
</div>
) : (
<div className="text-sm text-muted-foreground leading-relaxed">
{children}
</div>
)}
</div>
) : null}
</div>
);
}
Stories
Explorez chaque variante et etat dans le Storybook interactif :
Apercu
Basculez entre clair et sombre pour inspecter l'apercu Storybook integre.