feat: add search functionality to Playlist component for filtering tracks
This commit is contained in:
@@ -1,10 +1,13 @@
|
|||||||
|
import { useState } from 'react'
|
||||||
import { Badge } from '@/components/ui/badge'
|
import { Badge } from '@/components/ui/badge'
|
||||||
|
import { Button } from '@/components/ui/button'
|
||||||
|
import { Input } from '@/components/ui/input'
|
||||||
import { ScrollArea } from '@/components/ui/scroll-area'
|
import { ScrollArea } from '@/components/ui/scroll-area'
|
||||||
import { filesService } from '@/lib/api/services/files'
|
import { filesService } from '@/lib/api/services/files'
|
||||||
import { type PlayerPlaylist } from '@/lib/api/services/player'
|
import { type PlayerPlaylist } from '@/lib/api/services/player'
|
||||||
import { cn } from '@/lib/utils'
|
import { cn } from '@/lib/utils'
|
||||||
import { formatDuration } from '@/utils/format-duration'
|
import { formatDuration } from '@/utils/format-duration'
|
||||||
import { Music, Play } from 'lucide-react'
|
import { Music, Play, Search, X } from 'lucide-react'
|
||||||
|
|
||||||
interface PlaylistProps {
|
interface PlaylistProps {
|
||||||
playlist: PlayerPlaylist
|
playlist: PlayerPlaylist
|
||||||
@@ -19,6 +22,12 @@ export function Playlist({
|
|||||||
onTrackSelect,
|
onTrackSelect,
|
||||||
variant = 'normal',
|
variant = 'normal',
|
||||||
}: PlaylistProps) {
|
}: PlaylistProps) {
|
||||||
|
const [searchQuery, setSearchQuery] = useState('')
|
||||||
|
|
||||||
|
const filteredSounds = playlist.sounds.filter((sound) =>
|
||||||
|
sound.name.toLowerCase().includes(searchQuery.toLowerCase())
|
||||||
|
)
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<div className="w-full">
|
<div className="w-full">
|
||||||
{/* Header */}
|
{/* Header */}
|
||||||
@@ -29,28 +38,52 @@ export function Playlist({
|
|||||||
</Badge>
|
</Badge>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
|
{/* Search */}
|
||||||
|
<div className="relative mb-2">
|
||||||
|
<Search className="absolute left-2 top-1/2 -translate-y-1/2 h-3.5 w-3.5 text-muted-foreground" />
|
||||||
|
<Input
|
||||||
|
type="text"
|
||||||
|
placeholder="Search tracks..."
|
||||||
|
value={searchQuery}
|
||||||
|
onChange={(e) => setSearchQuery(e.target.value)}
|
||||||
|
className="h-8 pl-8 pr-8 text-xs"
|
||||||
|
/>
|
||||||
|
{searchQuery && (
|
||||||
|
<Button
|
||||||
|
variant="ghost"
|
||||||
|
size="icon"
|
||||||
|
className="absolute right-0 top-1/2 -translate-y-1/2 h-8 w-8"
|
||||||
|
onClick={() => setSearchQuery('')}
|
||||||
|
>
|
||||||
|
<X className="h-3.5 w-3.5" />
|
||||||
|
</Button>
|
||||||
|
)}
|
||||||
|
</div>
|
||||||
|
|
||||||
{/* Track List */}
|
{/* Track List */}
|
||||||
<ScrollArea
|
<ScrollArea
|
||||||
className={variant === 'maximized' ? 'h-[calc(100vh-230px)]' : 'h-60'}
|
className={variant === 'maximized' ? 'h-[calc(100vh-280px)]' : 'h-60'}
|
||||||
>
|
>
|
||||||
<div className="w-full">
|
<div className="w-full">
|
||||||
{playlist.sounds.map((sound, index) => (
|
{filteredSounds.map((sound) => {
|
||||||
<div
|
const originalIndex = playlist.sounds.findIndex((s) => s.id === sound.id)
|
||||||
key={sound.id}
|
return (
|
||||||
className={cn(
|
<div
|
||||||
'grid grid-cols-10 gap-2 items-center py-1.5 px-2 rounded hover:bg-muted/50 cursor-pointer text-xs',
|
key={sound.id}
|
||||||
currentIndex === index && 'bg-primary/10 text-primary',
|
className={cn(
|
||||||
)}
|
'grid grid-cols-10 gap-2 items-center py-1.5 px-2 rounded hover:bg-muted/50 cursor-pointer text-xs',
|
||||||
onClick={() => onTrackSelect(index)}
|
currentIndex === originalIndex && 'bg-primary/10 text-primary',
|
||||||
>
|
|
||||||
{/* Track number/play icon - 1 column */}
|
|
||||||
<div className="col-span-1 flex justify-center">
|
|
||||||
{currentIndex === index ? (
|
|
||||||
<Play className="h-3 w-3" />
|
|
||||||
) : (
|
|
||||||
<span className="text-muted-foreground">{index + 1}</span>
|
|
||||||
)}
|
)}
|
||||||
</div>
|
onClick={() => onTrackSelect(originalIndex)}
|
||||||
|
>
|
||||||
|
{/* Track number/play icon - 1 column */}
|
||||||
|
<div className="col-span-1 flex justify-center">
|
||||||
|
{currentIndex === originalIndex ? (
|
||||||
|
<Play className="h-3 w-3" />
|
||||||
|
) : (
|
||||||
|
<span className="text-muted-foreground">{originalIndex + 1}</span>
|
||||||
|
)}
|
||||||
|
</div>
|
||||||
|
|
||||||
{/* Thumbnail - 1 column */}
|
{/* Thumbnail - 1 column */}
|
||||||
<div className="col-span-1">
|
<div className="col-span-1">
|
||||||
@@ -78,7 +111,7 @@ export function Playlist({
|
|||||||
className={cn(
|
className={cn(
|
||||||
'font-medium truncate block',
|
'font-medium truncate block',
|
||||||
variant === 'maximized' ? 'text-sm' : 'text-xs',
|
variant === 'maximized' ? 'text-sm' : 'text-xs',
|
||||||
currentIndex === index ? 'text-primary' : 'text-foreground',
|
currentIndex === originalIndex ? 'text-primary' : 'text-foreground',
|
||||||
)}
|
)}
|
||||||
>
|
>
|
||||||
{sound.name}
|
{sound.name}
|
||||||
@@ -92,7 +125,7 @@ export function Playlist({
|
|||||||
</span>
|
</span>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
))}
|
)})}
|
||||||
</div>
|
</div>
|
||||||
</ScrollArea>
|
</ScrollArea>
|
||||||
|
|
||||||
|
|||||||
Reference in New Issue
Block a user