Import source: src/components/TrendingTracks/TrackRow
Import the existing local project snapshot. This commit organizes existing files; it does not represent newly developed functionality.
This commit is contained in:
@@ -0,0 +1,79 @@
|
|||||||
|
import { useState } from 'react'
|
||||||
|
import { motion } from 'framer-motion'
|
||||||
|
|
||||||
|
export default function TrackRow({ track, index, isActive, onPlay }) {
|
||||||
|
const [hovered, setHovered] = useState(false)
|
||||||
|
|
||||||
|
return (
|
||||||
|
<motion.div
|
||||||
|
onMouseEnter={() => setHovered(true)}
|
||||||
|
onMouseLeave={() => setHovered(false)}
|
||||||
|
onClick={() => onPlay(track)}
|
||||||
|
animate={{ x: hovered ? 6 : 0, backgroundColor: hovered ? 'rgba(201,168,76,0.035)' : 'transparent' }}
|
||||||
|
transition={{ duration: 0.2 }}
|
||||||
|
className="flex items-center gap-4 px-4 py-3.5 rounded-xl cursor-pointer"
|
||||||
|
style={isActive ? { background: 'rgba(201,168,76,0.06)', border: '1px solid rgba(201,168,76,0.18)' }
|
||||||
|
: { border: '1px solid transparent' }}
|
||||||
|
>
|
||||||
|
{/* Rank / eq bars */}
|
||||||
|
<div className="w-5 flex items-center justify-center flex-shrink-0">
|
||||||
|
{isActive ? (
|
||||||
|
<div className="flex items-end gap-px h-3.5">
|
||||||
|
{[1,2,3].map(i => (
|
||||||
|
<motion.span key={i}
|
||||||
|
animate={{ scaleY: [1, 0.25, 1] }}
|
||||||
|
transition={{ repeat: Infinity, duration: 0.65, delay: i * 0.18, ease: 'easeInOut' }}
|
||||||
|
className="block w-0.5 rounded-full origin-bottom"
|
||||||
|
style={{ height: '100%', background: 'var(--gold)' }}
|
||||||
|
/>
|
||||||
|
))}
|
||||||
|
</div>
|
||||||
|
) : (
|
||||||
|
<span className="text-xs font-mono text-muted">{index + 1}</span>
|
||||||
|
)}
|
||||||
|
</div>
|
||||||
|
|
||||||
|
{/* Cover */}
|
||||||
|
<div className="relative w-11 h-11 rounded-lg overflow-hidden flex-shrink-0 shadow-card">
|
||||||
|
<img src={track.cover} alt={track.title}
|
||||||
|
className="w-full h-full object-cover transition-transform duration-300"
|
||||||
|
style={{ transform: hovered ? 'scale(1.12)' : 'scale(1)' }} />
|
||||||
|
{hovered && !isActive && (
|
||||||
|
<motion.div initial={{ opacity: 0 }} animate={{ opacity: 1 }}
|
||||||
|
className="absolute inset-0 flex items-center justify-center"
|
||||||
|
style={{ background: 'rgba(7,7,9,0.55)' }}>
|
||||||
|
<svg className="w-3.5 h-3.5 ml-0.5" fill="var(--gold)" viewBox="0 0 24 24">
|
||||||
|
<path d="M8 5v14l11-7z" />
|
||||||
|
</svg>
|
||||||
|
</motion.div>
|
||||||
|
)}
|
||||||
|
</div>
|
||||||
|
|
||||||
|
{/* Title & artist */}
|
||||||
|
<div className="flex-1 min-w-0">
|
||||||
|
<p className={`text-sm font-medium truncate transition-colors duration-200 ${
|
||||||
|
isActive ? 'gold-text' : hovered ? 'text-white' : 'text-white'
|
||||||
|
}`}>{track.title}</p>
|
||||||
|
<p className="text-xs text-secondary truncate mt-0.5">{track.artist} · {track.album}</p>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
{/* Plays */}
|
||||||
|
<span className="hidden sm:block text-xs text-muted font-mono w-14 text-right">{track.plays}</span>
|
||||||
|
|
||||||
|
{/* Duration */}
|
||||||
|
<span className="text-xs text-secondary font-mono w-9 text-right">{track.duration}</span>
|
||||||
|
|
||||||
|
{/* Action button */}
|
||||||
|
<motion.button
|
||||||
|
animate={{ opacity: hovered ? 1 : 0, scale: hovered ? 1 : 0.75 }}
|
||||||
|
transition={{ duration: 0.18 }}
|
||||||
|
className="w-7 h-7 rounded-full flex items-center justify-center flex-shrink-0"
|
||||||
|
style={{ background: 'rgba(201,168,76,0.12)', border: '1px solid rgba(201,168,76,0.25)' }}
|
||||||
|
>
|
||||||
|
<svg className="w-3 h-3" fill="none" stroke="var(--gold)" viewBox="0 0 24 24">
|
||||||
|
<path strokeLinecap="round" strokeLinejoin="round" strokeWidth={2} d="M5 12h14M12 5l7 7-7 7" />
|
||||||
|
</svg>
|
||||||
|
</motion.button>
|
||||||
|
</motion.div>
|
||||||
|
)
|
||||||
|
}
|
||||||
Reference in New Issue
Block a user