Components
Loading preview...
Professional timeline for video editing and media management
@nparashar150
npx shadcn@latest add https://21st.dev/r/nparashar150/timeline-1'use client';
import { Button } from '@/components/ui/button';
import { Timeline, TimelineTrack } from '@/components/ui/timeline-1';
import { createTimeline } from '@/lib/utils';
import { Github, Moon, Sun } from 'lucide-react';
import { useEffect, useState } from 'react';
export default function TimelineDemo() {
const [tracks, setTracks] = useState<TimelineTrack[]>([
{
id: 'empty-video',
name: 'Video Track',
type: 'video',
height: 60,
visible: true,
items: [],
},
{
id: 'empty-audio',
name: 'Audio Track',
type: 'audio',
height: 60,
visible: true,
items: [],
},
]);
const [duration, setDuration] = useState(30);
const [currentTime, setCurrentTime] = useState(0);
const [isLoadingExample, setIsLoadingExample] = useState(false);
const [theme, setTheme] = useState<'light' | 'dark'>('light');
useEffect(() => {
const savedTheme = localStorage.getItem('theme') as 'light' | 'dark' | null;
const prefersDark = window.matchMedia('(prefers-color-scheme: dark)').matches;
const initialTheme = savedTheme || (prefersDark ? 'dark' : 'light');
setTheme(initialTheme);
document.documentElement.classList.toggle('dark', initialTheme === 'dark');
}, []);
const toggleTheme = () => {
const newTheme = theme === 'light' ? 'dark' : 'light';
setTheme(newTheme);
localStorage.setItem('theme', newTheme);
document.documentElement.classList.toggle('dark', newTheme === 'dark');
};
const handleLoadExample = () => {
setIsLoadingExample(true);
const randomComplexity = Math.max(2, Math.floor(Math.random() * 6));
const randomDuration = 60 + Math.random() * 240;
const randomSeed = Math.floor(Math.random() * 1000000);
const generatedTimeline = createTimeline({
complexity: randomComplexity,
duration: randomDuration,
seed: randomSeed,
});
setTracks(generatedTimeline.tracks);
setDuration(generatedTimeline.duration);
setCurrentTime(0);
setIsLoadingExample(false);
};
return (
<div className="relative flex h-screen flex-col overflow-hidden bg-background transition-colors">
{/* Header */}
<header className="relative z-10 flex flex-wrap items-center justify-between gap-6 px-6 pb-4 pt-6 sm:px-8">
<div>
<h1 className="text-3xl font-bold">Timeline Component</h1>
<p className="mt-2 text-muted-foreground">
Professional timeline for video editing and media management
</p>
</div>
<div className="flex items-center gap-3">
<Button asChild variant="outline" className="gap-2">
<a href="https://github.com/nparashar150/21st" target="_blank" rel="noopener noreferrer">
<Github className="h-4 w-4" />
GitHub
</a>
</Button>
<Button
size="icon"
variant="outline"
onClick={toggleTheme}
aria-label="Toggle theme"
>
{theme === 'light' ? <Moon className="h-4 w-4" /> : <Sun className="h-4 w-4" />}
</Button>
</div>
</header>
{/* Main Content */}
<main className="relative z-10 flex flex-1 flex-col gap-4 px-6 pb-6 sm:px-8">
{/* Timeline Container */}
<Timeline
tracks={tracks}
duration={duration}
onTimeUpdate={setCurrentTime}
onTracksChange={setTracks}
onDurationChange={setDuration}
showLoadExample
onLoadExample={handleLoadExample}
isLoadingExample={isLoadingExample}
className="border-0 h-full"
/>
{/* Info Cards */}
<div className="grid gap-4 sm:grid-cols-3">
<div className="rounded-lg border bg-card p-4">
<p className="text-sm font-medium text-muted-foreground">Current Time</p>
<p className="mt-1 text-2xl font-bold">{currentTime.toFixed(2)}s</p>
</div>
<div className="rounded-lg border bg-card p-4">
<p className="text-sm font-medium text-muted-foreground">Total Tracks</p>
<p className="mt-1 text-2xl font-bold">{tracks.length}</p>
</div>
<div className="rounded-lg border bg-card p-4">
<p className="text-sm font-medium text-muted-foreground">Duration</p>
<p className="mt-1 text-2xl font-bold">{Math.round(duration)}s</p>
</div>
</div>
</main>
</div>
);
}