feat(ui): hover cards, Spotify player layout, scrollbar tokens, context menu integration
HoverCard component (new):
- Rich preview cards on hover with framer-motion animation
- Viewport-aware positioning, portal rendering, open/close delays
- UserHoverContent: Discord-style user preview (avatar, bio, stats, follow)
- TrackHoverContent: Spotify-style track preview (cover, stats, play)
Audio player — Spotify-like 3-column layout:
- grid-cols-3 layout: track info | controls | volume+queue
- Progress bar moved to top edge (minimal variant)
- Glassmorphism (bg-background/95 backdrop-blur-md)
- Prominent centered play button (h-10 w-10 rounded-full, active:scale-95)
- Title marquee animation for long track names
- Reduced padding for tighter premium feel
Scrollbar styling:
- Migrated hardcoded rgba() to semantic tokens via color-mix(in oklch)
- Added transition on thumb hover for smooth visual feedback
ContextMenu integration:
- TrackListRow wrapped with ContextMenu (play, like, more actions)
- Dynamic items based on available callbacks
Co-authored-by: Cursor <cursoragent@cursor.com>
2026-02-09 22:18:46 +00:00
|
|
|
import { useRef, useState, useEffect } from 'react';
|
|
|
|
|
import { cn } from '@/lib/utils';
|
|
|
|
|
|
2026-02-05 21:40:06 +00:00
|
|
|
interface Track {
|
|
|
|
|
title?: string;
|
|
|
|
|
artist?: string;
|
2026-02-09 23:11:11 +00:00
|
|
|
artistId?: string;
|
2026-02-05 21:40:06 +00:00
|
|
|
cover?: string;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
interface AudioPlayerTrackInfoProps {
|
|
|
|
|
track: Track;
|
2026-02-09 23:11:11 +00:00
|
|
|
onArtistClick?: (artistId: string) => void;
|
2026-02-05 21:40:06 +00:00
|
|
|
}
|
|
|
|
|
|
2026-02-09 23:11:11 +00:00
|
|
|
export function AudioPlayerTrackInfo({ track, onArtistClick }: AudioPlayerTrackInfoProps) {
|
feat(ui): hover cards, Spotify player layout, scrollbar tokens, context menu integration
HoverCard component (new):
- Rich preview cards on hover with framer-motion animation
- Viewport-aware positioning, portal rendering, open/close delays
- UserHoverContent: Discord-style user preview (avatar, bio, stats, follow)
- TrackHoverContent: Spotify-style track preview (cover, stats, play)
Audio player — Spotify-like 3-column layout:
- grid-cols-3 layout: track info | controls | volume+queue
- Progress bar moved to top edge (minimal variant)
- Glassmorphism (bg-background/95 backdrop-blur-md)
- Prominent centered play button (h-10 w-10 rounded-full, active:scale-95)
- Title marquee animation for long track names
- Reduced padding for tighter premium feel
Scrollbar styling:
- Migrated hardcoded rgba() to semantic tokens via color-mix(in oklch)
- Added transition on thumb hover for smooth visual feedback
ContextMenu integration:
- TrackListRow wrapped with ContextMenu (play, like, more actions)
- Dynamic items based on available callbacks
Co-authored-by: Cursor <cursoragent@cursor.com>
2026-02-09 22:18:46 +00:00
|
|
|
const titleRef = useRef<HTMLParagraphElement>(null);
|
|
|
|
|
const [isOverflowing, setIsOverflowing] = useState(false);
|
|
|
|
|
|
|
|
|
|
useEffect(() => {
|
|
|
|
|
const el = titleRef.current;
|
|
|
|
|
if (el) {
|
|
|
|
|
setIsOverflowing(el.scrollWidth > el.clientWidth);
|
|
|
|
|
}
|
|
|
|
|
}, [track.title]);
|
|
|
|
|
|
2026-02-05 21:40:06 +00:00
|
|
|
return (
|
2026-02-09 23:11:11 +00:00
|
|
|
<div className="flex items-center gap-3 min-w-0 group/track">
|
|
|
|
|
{/* Album art with ambient glow */}
|
2026-02-05 21:40:06 +00:00
|
|
|
{track.cover && (
|
2026-02-09 23:11:11 +00:00
|
|
|
<div className="relative shrink-0">
|
|
|
|
|
{/* Ambient glow */}
|
|
|
|
|
<div className="absolute inset-0 rounded-lg bg-primary/20 blur-xl scale-150 opacity-0 group-hover/track:opacity-100 transition-opacity duration-500" />
|
|
|
|
|
<img
|
|
|
|
|
src={track.cover}
|
|
|
|
|
alt={track.title ?? ''}
|
|
|
|
|
className="relative z-10 w-10 h-10 rounded object-cover"
|
|
|
|
|
/>
|
|
|
|
|
</div>
|
2026-02-05 21:40:06 +00:00
|
|
|
)}
|
|
|
|
|
<div className="min-w-0 flex-1">
|
feat(ui): hover cards, Spotify player layout, scrollbar tokens, context menu integration
HoverCard component (new):
- Rich preview cards on hover with framer-motion animation
- Viewport-aware positioning, portal rendering, open/close delays
- UserHoverContent: Discord-style user preview (avatar, bio, stats, follow)
- TrackHoverContent: Spotify-style track preview (cover, stats, play)
Audio player — Spotify-like 3-column layout:
- grid-cols-3 layout: track info | controls | volume+queue
- Progress bar moved to top edge (minimal variant)
- Glassmorphism (bg-background/95 backdrop-blur-md)
- Prominent centered play button (h-10 w-10 rounded-full, active:scale-95)
- Title marquee animation for long track names
- Reduced padding for tighter premium feel
Scrollbar styling:
- Migrated hardcoded rgba() to semantic tokens via color-mix(in oklch)
- Added transition on thumb hover for smooth visual feedback
ContextMenu integration:
- TrackListRow wrapped with ContextMenu (play, like, more actions)
- Dynamic items based on available callbacks
Co-authored-by: Cursor <cursoragent@cursor.com>
2026-02-09 22:18:46 +00:00
|
|
|
<div className="overflow-hidden">
|
|
|
|
|
<p
|
|
|
|
|
ref={titleRef}
|
|
|
|
|
className={cn(
|
|
|
|
|
'text-sm font-medium text-foreground whitespace-nowrap',
|
|
|
|
|
isOverflowing
|
|
|
|
|
? 'animate-marquee'
|
|
|
|
|
: 'truncate',
|
|
|
|
|
)}
|
|
|
|
|
>
|
|
|
|
|
{track.title}
|
|
|
|
|
</p>
|
|
|
|
|
</div>
|
2026-02-09 23:11:11 +00:00
|
|
|
{track.artist && (
|
fix(a11y): Sprint 7 — semantic HTML and accessibility deep-dive
S7.1: Replace div onClick with semantic button in DialogTrigger.tsx
S7.2: Replace role="button" divs with native <button> elements in 12 files
(PlaylistCard, TrackCard, ConversationItem, NotificationMenuItem,
AudioPlayerTrackInfo, SearchPageResults, ProjectsManagerAddCard,
ProjectsManagerCard, GearInventoryGrid, UploadModal, dropdown.tsx,
LibraryPageGrid)
S7.3: Add focus-visible:ring-2 to 14 form inputs with outline-none across
9 modal files (CreateGroupModal, DataExportModal, EditPlaylistModal,
AddToPlaylistModal, BanUserModal, RefundRequestModal, FlashSaleModal,
TipStreamerModal, CreatePostModal)
S7.4: Add semantic landmarks — <section> in DashboardPage, <article> in
PostCard and CourseCard
Co-authored-by: Cursor <cursoragent@cursor.com>
2026-02-12 09:34:39 +00:00
|
|
|
<button
|
|
|
|
|
type="button"
|
|
|
|
|
className="appearance-none bg-transparent border-0 p-0 text-xs text-muted-foreground truncate hover:text-foreground hover:underline transition-colors cursor-pointer text-left"
|
2026-02-09 23:11:11 +00:00
|
|
|
onClick={() => onArtistClick?.(track.artistId ?? '')}
|
|
|
|
|
>
|
|
|
|
|
{track.artist}
|
fix(a11y): Sprint 7 — semantic HTML and accessibility deep-dive
S7.1: Replace div onClick with semantic button in DialogTrigger.tsx
S7.2: Replace role="button" divs with native <button> elements in 12 files
(PlaylistCard, TrackCard, ConversationItem, NotificationMenuItem,
AudioPlayerTrackInfo, SearchPageResults, ProjectsManagerAddCard,
ProjectsManagerCard, GearInventoryGrid, UploadModal, dropdown.tsx,
LibraryPageGrid)
S7.3: Add focus-visible:ring-2 to 14 form inputs with outline-none across
9 modal files (CreateGroupModal, DataExportModal, EditPlaylistModal,
AddToPlaylistModal, BanUserModal, RefundRequestModal, FlashSaleModal,
TipStreamerModal, CreatePostModal)
S7.4: Add semantic landmarks — <section> in DashboardPage, <article> in
PostCard and CourseCard
Co-authored-by: Cursor <cursoragent@cursor.com>
2026-02-12 09:34:39 +00:00
|
|
|
</button>
|
2026-02-09 23:11:11 +00:00
|
|
|
)}
|
2026-02-05 21:40:06 +00:00
|
|
|
</div>
|
|
|
|
|
</div>
|
|
|
|
|
);
|
|
|
|
|
}
|