Import source: src/components/Player/PlayerBar

Import the existing local project snapshot. This commit organizes existing files; it does not represent newly developed functionality.
This commit is contained in:
arsam
2026-09-18 23:08:05 +03:30
parent 397588dff2
commit 36d1711fb1
+146
View File
@@ -0,0 +1,146 @@
import { useState } from 'react'
import { motion, AnimatePresence } from 'framer-motion'
import Waveform3D from './Waveform3D'
export default function PlayerBar({ currentTrack, isPlaying, progress, volume, onToggle, onNext, onPrev, onSeek, onVolume }) {
const [liked, setLiked] = useState(false)
const [shuffled, setShuffled] = useState(false)
const [repeated, setRepeated] = useState(false)
if (!currentTrack) return null
const pct = `${(progress * 100).toFixed(1)}%`
return (
<AnimatePresence>
<motion.footer
initial={{ y: 100, opacity: 0 }}
animate={{ y: 0, opacity: 1 }}
transition={{ duration: 0.55, ease: [0.22, 1, 0.36, 1] }}
className="fixed bottom-0 left-0 right-0 z-50 glass-dark"
style={{ borderTop: '1px solid rgba(201,168,76,0.1)', boxShadow: '0 -16px 48px rgba(0,0,0,0.7)' }}
>
<div className="max-w-7xl mx-auto px-5 h-20 flex items-center gap-4 md:gap-6">
{/* ── Track info ──────────────────── */}
<div className="flex items-center gap-3 w-48 md:w-56 flex-shrink-0 min-w-0">
<div className="relative w-11 h-11 rounded-lg overflow-hidden flex-shrink-0 shadow-card">
<img src={currentTrack.cover} alt={currentTrack.title} className="w-full h-full object-cover" />
{isPlaying && (
<div className="absolute inset-0 rounded-lg"
style={{ boxShadow: 'inset 0 0 0 1.5px rgba(201,168,76,0.6)', animation: 'pulse-gold 2s ease-in-out infinite' }} />
)}
</div>
<div className="min-w-0 flex-1">
<p className="text-white text-xs font-semibold truncate leading-snug">{currentTrack.title}</p>
<p className="text-secondary text-xs truncate">{currentTrack.artist}</p>
</div>
<motion.button
whileHover={{ scale: 1.2 }}
whileTap={{ scale: 0.85 }}
onClick={() => setLiked(l => !l)}
className="flex-shrink-0 transition-colors"
style={{ color: liked ? 'var(--gold)' : 'var(--text-muted)' }}
>
<svg className="w-3.5 h-3.5" fill={liked ? 'currentColor' : 'none'}
stroke="currentColor" viewBox="0 0 24 24">
<path strokeLinecap="round" strokeLinejoin="round" strokeWidth={1.8}
d="M4.318 6.318a4.5 4.5 0 000 6.364L12 20.364l7.682-7.682a4.5 4.5 0 00-6.364-6.364L12 7.636l-1.318-1.318a4.5 4.5 0 00-6.364 0z" />
</svg>
</motion.button>
</div>
{/* ── Centre: controls + progress ─ */}
<div className="flex-1 flex flex-col items-center gap-1.5 min-w-0">
{/* Control buttons */}
<div className="flex items-center gap-5">
{/* Shuffle */}
<motion.button whileHover={{ scale: 1.15 }} whileTap={{ scale: 0.88 }}
onClick={() => setShuffled(s => !s)}
style={{ color: shuffled ? 'var(--gold)' : 'var(--text-muted)' }}
className="hidden sm:block transition-colors">
<svg className="w-3.5 h-3.5" fill="none" stroke="currentColor" viewBox="0 0 24 24">
<path strokeLinecap="round" strokeLinejoin="round" strokeWidth={1.8}
d="M16 3l4 4-4 4M20 7H4M8 21l-4-4 4-4M4 17h16" />
</svg>
</motion.button>
{/* Prev */}
<motion.button whileHover={{ scale: 1.12 }} whileTap={{ scale: 0.88 }}
onClick={onPrev} className="text-secondary hover:text-white transition-colors">
<svg className="w-4 h-4" fill="currentColor" viewBox="0 0 24 24">
<path d="M6 6h2v12H6zm3.5 6 8.5 6V6z" />
</svg>
</motion.button>
{/* Play/Pause */}
<motion.button
whileHover={{ scale: 1.08, boxShadow: '0 0 28px rgba(201,168,76,0.45)' }}
whileTap={{ scale: 0.92 }}
onClick={onToggle}
className="w-9 h-9 rounded-full flex items-center justify-center text-dark-base shadow-gold-sm"
style={{ background: 'linear-gradient(135deg, #c9a84c, #e8c547)' }}
>
{isPlaying
? <svg className="w-3.5 h-3.5" fill="currentColor" viewBox="0 0 24 24"><path d="M6 19h4V5H6v14zm8-14v14h4V5h-4z" /></svg>
: <svg className="w-3.5 h-3.5 ml-0.5" fill="currentColor" viewBox="0 0 24 24"><path d="M8 5v14l11-7z" /></svg>
}
</motion.button>
{/* Next */}
<motion.button whileHover={{ scale: 1.12 }} whileTap={{ scale: 0.88 }}
onClick={onNext} className="text-secondary hover:text-white transition-colors">
<svg className="w-4 h-4" fill="currentColor" viewBox="0 0 24 24">
<path d="M6 18l8.5-6L6 6v12zM16 6v12h2V6h-2z" />
</svg>
</motion.button>
{/* Repeat */}
<motion.button whileHover={{ scale: 1.15 }} whileTap={{ scale: 0.88 }}
onClick={() => setRepeated(r => !r)}
style={{ color: repeated ? 'var(--gold)' : 'var(--text-muted)' }}
className="hidden sm:block transition-colors">
<svg className="w-3.5 h-3.5" fill="none" stroke="currentColor" viewBox="0 0 24 24">
<path strokeLinecap="round" strokeLinejoin="round" strokeWidth={1.8}
d="M17 1l4 4-4 4M3 11V9a4 4 0 014-4h14M7 23l-4-4 4-4M21 13v2a4 4 0 01-4 4H3" />
</svg>
</motion.button>
</div>
{/* Progress bar */}
<div className="w-full max-w-md flex items-center gap-2.5">
<span className="text-xs text-muted font-mono w-8 text-right flex-shrink-0">0:00</span>
<div className="flex-1 h-1 rounded-full cursor-pointer group relative"
style={{ background: 'var(--dark-border)' }}
onClick={e => {
const r = e.currentTarget.getBoundingClientRect()
onSeek((e.clientX - r.left) / r.width)
}}>
<div className="h-full rounded-full relative transition-all"
style={{ width: pct, background: 'linear-gradient(90deg, #c9a84c, #e8c547)' }}>
<div className="absolute right-0 top-1/2 -translate-y-1/2 w-3 h-3 rounded-full bg-white scale-0 group-hover:scale-100 transition-transform shadow-gold-sm" />
</div>
</div>
<span className="text-xs text-muted font-mono w-8 flex-shrink-0">{currentTrack.duration}</span>
</div>
</div>
{/* ── Right: waveform + volume ─────── */}
<div className="hidden lg:flex items-center gap-4 w-52 flex-shrink-0 justify-end">
<div className="w-24 h-9">
<Waveform3D isPlaying={isPlaying} />
</div>
<div className="flex items-center gap-2">
<svg className="w-3.5 h-3.5 text-muted flex-shrink-0" fill="currentColor" viewBox="0 0 24 24">
<path d="M3 9v6h4l5 5V4L7 9H3zm13.5 3c0-1.77-1.02-3.29-2.5-4.03v8.05c1.48-.73 2.5-2.25 2.5-4.02z" />
</svg>
<input type="range" min="0" max="1" step="0.01"
value={volume} onChange={e => onVolume(parseFloat(e.target.value))}
className="w-20" style={{ accentColor: 'var(--gold)' }} />
</div>
</div>
</div>
</motion.footer>
</AnimatePresence>
)
}