feat: Smart play/pause overlay on DetailPanel album art

DetailPanel.tsx:
- Added isPlaying and currentClipId props to track playback state
- Added onTogglePlay prop for pausing currently-playing track
- Album art click handler now smart:
  * If this track IS playing → pause it (onTogglePlay)
  * If this track is NOT playing → play it (onPlay)
- Hover overlay icon adapts dynamically:
  * Currently playing + hover → shows PAUSE icon (⏸)
  * Not playing + hover → shows PLAY icon (▶)
- Works for both artwork images and placeholder state

App.tsx:
- Passes isPlaying, currentClipId, and onTogglePlay to DetailPanel

Visual states:
- Different track in panel: ▶ on hover (click to play)
- Same track playing: ⏸ on hover (click to pause)
- Same track paused: ▶ on hover (click to resume)
This commit is contained in:
rin.kagamine committed 2026-05-29 21:49:36 -04:00
1 parent 4808406113
commit fa3515acc9
2 files changed
+39 -9

No files matched your search

+3
View File
@@ -229,12 +229,15 @@ function App() {
{selectedClip && (
<DetailPanel
clip={selectedClip}
isPlaying={isPlaying}
currentClipId={currentClip?.id}
onClose={() => setSelectedClip(null)}
onRename={handleRename}
onDelete={handleDelete}
onUploadArtwork={handleUploadArtwork}
onRemoveArtwork={handleRemoveArtwork}
onPlay={handlePlay}
onTogglePlay={() => setIsPlaying(!isPlaying)}
/>
)}
+36 -9
View File
@@ -18,12 +18,15 @@ interface Clip {
interface Props {
clip: Clip
isPlaying: boolean
currentClipId?: string
onClose: () => void
onRename: (id: string, title: string) => void
onDelete: (id: string) => void
onUploadArtwork: (id: string, file: File) => void
onRemoveArtwork: (id: string) => void
onPlay?: (clip: Clip) => void
onTogglePlay?: () => void
}
const modelName = (modelId: string) => {
@@ -34,7 +37,7 @@ const modelName = (modelId: string) => {
return names[modelId] || modelId
}
export default function DetailPanel({ clip, onClose, onRename, onDelete, onUploadArtwork, onRemoveArtwork, onPlay }: Props) {
export default function DetailPanel({ clip, isPlaying, currentClipId, onClose, onRename, onDelete, onUploadArtwork, onRemoveArtwork, onPlay, onTogglePlay }: Props) {
const artworkInputRef = useRef<HTMLInputElement>(null)
const [isEditingTitle, setIsEditingTitle] = useState(false)
const [editTitle, setEditTitle] = useState(clip.title || '')
@@ -149,7 +152,13 @@ export default function DetailPanel({ clip, onClose, onRename, onDelete, onUploa
{clip.artwork_filename ? (
<div
className="relative w-full aspect-square rounded-lg overflow-hidden cursor-pointer group"
onClick={() => onPlay?.(clip)}
onClick={() => {
if (currentClipId === clip.id && isPlaying) {
onTogglePlay?.()
} else {
onPlay?.(clip)
}
}}
>
<img
src={`/api/clips/${clip.id}/artwork`}
@@ -157,24 +166,42 @@ export default function DetailPanel({ clip, onClose, onRename, onDelete, onUploa
className="w-full h-full object-cover"
/>
<div className="absolute inset-0 bg-black/40 opacity-0 group-hover:opacity-100 transition-opacity flex items-center justify-center">
<svg className="w-12 h-12 text-white" fill="currentColor" viewBox="0 0 24 24">
<path d="M8 5v14l11-7z" />
</svg>
{currentClipId === clip.id && isPlaying ? (
<svg className="w-12 h-12 text-white" fill="currentColor" viewBox="0 0 24 24">
<path d="M6 19h4V5H6v14zm8-14v14h4V5h-4z" />
</svg>
) : (
<svg className="w-12 h-12 text-white" fill="currentColor" viewBox="0 0 24 24">
<path d="M8 5v14l11-7z" />
</svg>
)}
</div>
</div>
) : (
<div
className="relative w-full aspect-square bg-white/5 rounded-lg flex flex-col items-center justify-center text-lyria-accent/20 cursor-pointer group hover:bg-white/10 transition-colors"
onClick={() => onPlay?.(clip)}
onClick={() => {
if (currentClipId === clip.id && isPlaying) {
onTogglePlay?.()
} else {
onPlay?.(clip)
}
}}
>
<svg className="w-14 h-14" viewBox="0 0 24 24">
<text x="12" y="19" textAnchor="middle" fontSize="20" fill="currentColor">𝄞</text>
</svg>
<span className="text-xs mt-1 text-lyria-muted/40">No album art</span>
<div className="absolute inset-0 bg-black/40 opacity-0 group-hover:opacity-100 transition-opacity flex items-center justify-center">
<svg className="w-12 h-12 text-white" fill="currentColor" viewBox="0 0 24 24">
<path d="M8 5v14l11-7z" />
</svg>
{currentClipId === clip.id && isPlaying ? (
<svg className="w-12 h-12 text-white" fill="currentColor" viewBox="0 0 24 24">
<path d="M6 19h4V5H6v14zm8-14v14h4V5h-4z" />
</svg>
) : (
<svg className="w-12 h-12 text-white" fill="currentColor" viewBox="0 0 24 24">
<path d="M8 5v14l11-7z" />
</svg>
)}
</div>
</div>
)}