mirror of
https://github.com/KuekHaoYang/KVideo.git
synced 2026-08-21 03:33:42 +08:00
- Added EpisodeList component for displaying a list of episodes with selection functionality. - Created PlayerError component to handle and display video playback errors. - Developed VideoMetadata component to show detailed information about the video. - Implemented VideoPlayer component for video playback with error handling and loading states. - Introduced EmptyState and NoResults components for search results handling. - Created ResultsHeader component to display search results summary. - Developed SearchForm component for user input and search initiation. - Implemented SourceBadges component to show available video sources. - Created VideoGrid component to display search results in a grid format. - Added useSearchCache and useSearchStream hooks for managing search state and caching results. - Implemented useVideoPlayer hook for fetching and managing video details. - Added utility function to map source IDs to their respective names.
63 lines
2.1 KiB
TypeScript
63 lines
2.1 KiB
TypeScript
'use client';
|
|
|
|
import { Card } from '@/components/ui/Card';
|
|
import { Badge } from '@/components/ui/Badge';
|
|
import { Icons } from '@/components/ui/Icon';
|
|
|
|
interface Episode {
|
|
name?: string;
|
|
url: string;
|
|
}
|
|
|
|
interface EpisodeListProps {
|
|
episodes: Episode[] | null;
|
|
currentEpisode: number;
|
|
onEpisodeClick: (episode: Episode, index: number) => void;
|
|
}
|
|
|
|
export function EpisodeList({ episodes, currentEpisode, onEpisodeClick }: EpisodeListProps) {
|
|
return (
|
|
<Card hover={false} className="sticky top-32">
|
|
<h3 className="text-xl font-bold text-[var(--text-color)] mb-4 flex items-center gap-2">
|
|
<Icons.List size={24} />
|
|
<span>选集</span>
|
|
{episodes && (
|
|
<Badge variant="primary">{episodes.length}</Badge>
|
|
)}
|
|
</h3>
|
|
|
|
<div className="max-h-[600px] overflow-y-auto space-y-2 pr-2">
|
|
{episodes && episodes.length > 0 ? (
|
|
episodes.map((episode, index) => (
|
|
<button
|
|
key={index}
|
|
onClick={() => onEpisodeClick(episode, index)}
|
|
className={`
|
|
w-full px-4 py-3 rounded-[var(--radius-2xl)] text-left transition-[var(--transition-fluid)]
|
|
${currentEpisode === index
|
|
? 'bg-[var(--accent-color)] text-white shadow-[0_4px_12px_color-mix(in_srgb,var(--accent-color)_50%,transparent)] brightness-110'
|
|
: 'bg-[var(--glass-bg)] hover:bg-[var(--glass-hover)] text-[var(--text-color)] border border-[var(--glass-border)]'
|
|
}
|
|
`}
|
|
>
|
|
<div className="flex items-center justify-between">
|
|
<span className="font-medium">
|
|
{episode.name || `第 ${index + 1} 集`}
|
|
</span>
|
|
{currentEpisode === index && (
|
|
<Icons.Play size={16} />
|
|
)}
|
|
</div>
|
|
</button>
|
|
))
|
|
) : (
|
|
<div className="text-center py-8 text-[var(--text-secondary)]">
|
|
<Icons.Inbox size={48} className="text-[var(--text-color-secondary)] mx-auto mb-2" />
|
|
<p>暂无剧集信息</p>
|
|
</div>
|
|
)}
|
|
</div>
|
|
</Card>
|
|
);
|
|
}
|