feat(ux): improve song details panel UX - hide panel by default, show only when song selected, add close button with X icon

This commit is contained in:
Geert Rademakes 2025-08-15 13:38:45 +02:00
parent 96fdf64060
commit dc8254772c
2 changed files with 32 additions and 12 deletions

View File

@ -83,6 +83,11 @@ const RekordboxReader: React.FC = () => {
setSelectedSong(song); setSelectedSong(song);
}, []); }, []);
// Handle closing the song details panel
const handleCloseSongDetails = useCallback(() => {
setSelectedSong(null);
}, []);
// Handle playing a song from the main view // Handle playing a song from the main view
const handlePlaySong = useCallback((song: Song) => { const handlePlaySong = useCallback((song: Song) => {
// Check if song has S3 file // Check if song has S3 file
@ -685,7 +690,7 @@ const RekordboxReader: React.FC = () => {
</Box> </Box>
{/* Details Panel */} {/* Details Panel */}
{!isMobile && ( {!isMobile && selectedSong && (
<Box <Box
w="350px" w="350px"
minW="350px" minW="350px"
@ -706,7 +711,7 @@ const RekordboxReader: React.FC = () => {
'&::-webkit-scrollbar-track': { backgroundColor: 'var(--chakra-colors-gray-900)' }, '&::-webkit-scrollbar-track': { backgroundColor: 'var(--chakra-colors-gray-900)' },
}} }}
> >
<SongDetails song={selectedSong} /> <SongDetails song={selectedSong} onClose={handleCloseSongDetails} />
</Box> </Box>
)} )}
</Flex> </Flex>

View File

@ -1,10 +1,12 @@
import React, { useMemo, memo } from "react"; import React, { useMemo, memo } from "react";
import { Box, VStack, Text, Divider } from "@chakra-ui/react"; import { Box, VStack, Text, Divider, IconButton, HStack } from "@chakra-ui/react";
import { CloseIcon } from "@chakra-ui/icons";
import { Song } from "../types/interfaces"; import { Song } from "../types/interfaces";
import { formatDuration } from '../utils/formatters'; import { formatDuration } from '../utils/formatters';
interface SongDetailsProps { interface SongDetailsProps {
song: Song | null; song: Song | null;
onClose?: () => void;
} }
const calculateBitrate = (size: string, totalTime: string): number | null => { const calculateBitrate = (size: string, totalTime: string): number | null => {
@ -22,7 +24,7 @@ const calculateBitrate = (size: string, totalTime: string): number | null => {
return Math.round(bits / seconds / 1000); return Math.round(bits / seconds / 1000);
}; };
export const SongDetails: React.FC<SongDetailsProps> = memo(({ song }) => { export const SongDetails: React.FC<SongDetailsProps> = memo(({ song, onClose }) => {
// Memoize expensive calculations // Memoize expensive calculations
const songDetails = useMemo(() => { const songDetails = useMemo(() => {
if (!song) return null; if (!song) return null;
@ -72,14 +74,27 @@ export const SongDetails: React.FC<SongDetailsProps> = memo(({ song }) => {
return ( return (
<Box p={4} bg="gray.800" borderRadius="md"> <Box p={4} bg="gray.800" borderRadius="md">
<VStack align="stretch" spacing={4}> <VStack align="stretch" spacing={4}>
<Box> <HStack justify="space-between" align="flex-start">
<Text fontSize="lg" fontWeight="bold" color="white"> <Box flex={1}>
{song.title} <Text fontSize="lg" fontWeight="bold" color="white">
</Text> {song.title}
<Text fontSize="md" color="gray.400"> </Text>
{song.artist} <Text fontSize="md" color="gray.400">
</Text> {song.artist}
</Box> </Text>
</Box>
{onClose && (
<IconButton
size="sm"
variant="ghost"
icon={<CloseIcon />}
onClick={onClose}
aria-label="Close details"
color="gray.400"
_hover={{ bg: "gray.700", color: "white" }}
/>
)}
</HStack>
<Divider borderColor="gray.700" /> <Divider borderColor="gray.700" />
<VStack align="stretch" spacing={3}> <VStack align="stretch" spacing={3}>
{songDetails.details.map(({ label, value }) => ( {songDetails.details.map(({ label, value }) => (