UI fixes!

This commit is contained in:
Geert Rademakes 2025-04-25 09:21:38 +02:00
parent 1a3beb7e6f
commit 7e1f4e1cd4
2 changed files with 78 additions and 27 deletions

View File

@ -10,7 +10,6 @@ import {
ModalFooter, ModalFooter,
ModalHeader, ModalHeader,
ModalOverlay, ModalOverlay,
Text,
useDisclosure, useDisclosure,
VStack, VStack,
Menu, Menu,
@ -21,7 +20,7 @@ import {
Collapse, Collapse,
MenuDivider, MenuDivider,
MenuGroup, MenuGroup,
useToast Icon
} from "@chakra-ui/react"; } from "@chakra-ui/react";
import { ChevronDownIcon, DeleteIcon, ChevronRightIcon, AddIcon, ViewIcon } from "@chakra-ui/icons"; import { ChevronDownIcon, DeleteIcon, ChevronRightIcon, AddIcon, ViewIcon } from "@chakra-ui/icons";
import React, { useState, useCallback } from "react"; import React, { useState, useCallback } from "react";
@ -39,13 +38,16 @@ interface PlaylistManagerProps {
const getButtonStyles = (isSelected: boolean) => ({ const getButtonStyles = (isSelected: boolean) => ({
width: "100%", width: "100%",
justifyContent: "flex-start", display: "flex",
alignItems: "center",
justifyContent: "space-between",
bg: isSelected ? "blue.800" : "transparent", bg: isSelected ? "blue.800" : "transparent",
color: isSelected ? "white" : "gray.100", color: isSelected ? "white" : "gray.100",
fontWeight: isSelected ? "600" : "normal", fontWeight: isSelected ? "600" : "normal",
borderRadius: "md", borderRadius: "md",
px: 4, px: 4,
py: 2, py: 2,
cursor: "pointer",
transition: "all 0.2s", transition: "all 0.2s",
_hover: { _hover: {
bg: isSelected ? "blue.600" : "whiteAlpha.200", bg: isSelected ? "blue.600" : "whiteAlpha.200",
@ -76,7 +78,7 @@ const PlaylistItem: React.FC<PlaylistItemProps> = ({
allFolders, allFolders,
}) => { }) => {
const [isOpen, setIsOpen] = useState(true); const [isOpen, setIsOpen] = useState(true);
const indent = level * 20; // Increased indent per level const indent = level * 10;
if (node.type === 'folder') { if (node.type === 'folder') {
return ( return (
@ -86,19 +88,36 @@ const PlaylistItem: React.FC<PlaylistItemProps> = ({
flex={1} flex={1}
{...getButtonStyles(false)} {...getButtonStyles(false)}
onClick={() => setIsOpen(!isOpen)} onClick={() => setIsOpen(!isOpen)}
pl={indent} ml={indent}
justifyContent="flex-start"
leftIcon={ leftIcon={
<ChevronRightIcon <Box position="relative" display="flex" alignItems="center">
transform={isOpen ? 'rotate(90deg)' : 'none'} <ChevronRightIcon
transition="transform 0.2s" position="absolute"
/> right="-6px"
transform={isOpen ? 'rotate(90deg)' : 'none'}
transition="transform 0.2s"
color="gray.400"
/>
</Box>
} }
> >
<Icon
viewBox="0 0 24 24"
color="blue.300"
ml={1}
mr={2}
>
<path
fill="currentColor"
d="M20 6h-8l-2-2H4c-1.1 0-1.99.9-1.99 2L2 18c0 1.1.9 2 2 2h16c1.1 0 2-.9 2-2V8c0-1.1-.9-2-2-2zm0 12H4V8h16v10z"
/>
</Icon>
{node.name} {node.name}
</Button> </Button>
</Flex> </Flex>
<Collapse in={isOpen}> <Collapse in={isOpen}>
<VStack spacing={1} align="stretch"> <VStack spacing={1} mt={2} align="stretch">
{node.children?.map((child, index) => ( {node.children?.map((child, index) => (
<PlaylistItem <PlaylistItem
key={child.id || index} key={child.id || index}
@ -118,25 +137,43 @@ const PlaylistItem: React.FC<PlaylistItemProps> = ({
} }
return ( return (
<Flex align="center" gap={1}> <Flex align="center" gap={0}>
<Button <Button
flex={1} flex="1 1 auto"
{...getButtonStyles(selectedItem === node.name)} {...getButtonStyles(selectedItem === node.name)}
onClick={() => onPlaylistSelect(node.name)} onClick={() => onPlaylistSelect(node.name)}
pl={indent + 24} // Extra indent for playlists inside folders ml={indent}
borderRightRadius={0}
borderRight="1px solid"
borderRightColor="whiteAlpha.200"
> >
{node.name} {node.name}
</Button> </Button>
<Menu> <Menu>
<MenuButton <MenuButton
as={IconButton} as={Button}
width="32px"
height="40px"
borderLeftRadius={0}
bg={selectedItem === node.name ? "blue.800" : "transparent"}
color={selectedItem === node.name ? "white" : "gray.100"}
fontWeight={selectedItem === node.name ? "600" : "normal"}
transition="all 0.2s"
_hover={{
bg: selectedItem === node.name ? "blue.600" : "whiteAlpha.200",
transform: "none"
}}
_active={{
bg: selectedItem === node.name ? "blue.700" : "whiteAlpha.300",
}}
aria-label="Playlist options" aria-label="Playlist options"
icon={<ChevronDownIcon />} p={0}
variant="ghost" >
size="sm" <ChevronDownIcon
color="gray.400" color={selectedItem === node.name ? "white" : "gray.400"}
_hover={{ color: "white", bg: "whiteAlpha.200" }} _hover={{ color: "white" }}
/> />
</MenuButton>
<MenuList bg="gray.800" borderColor="gray.700"> <MenuList bg="gray.800" borderColor="gray.700">
<MenuGroup title="Move to folder"> <MenuGroup title="Move to folder">
<MenuItem <MenuItem

View File

@ -28,6 +28,7 @@ interface SongListProps {
onSongSelect: (song: Song) => void; onSongSelect: (song: Song) => void;
selectedSongId: string | null; selectedSongId: string | null;
currentPlaylist: string | null; currentPlaylist: string | null;
depth?: number;
} }
export const SongList: React.FC<SongListProps> = ({ export const SongList: React.FC<SongListProps> = ({
@ -37,7 +38,8 @@ export const SongList: React.FC<SongListProps> = ({
playlists, playlists,
onSongSelect, onSongSelect,
selectedSongId, selectedSongId,
currentPlaylist currentPlaylist,
depth = 0
}) => { }) => {
const [selectedSongs, setSelectedSongs] = useState<Set<string>>(new Set()); const [selectedSongs, setSelectedSongs] = useState<Set<string>>(new Set());
const [searchQuery, setSearchQuery] = useState(""); const [searchQuery, setSearchQuery] = useState("");
@ -147,8 +149,9 @@ export const SongList: React.FC<SongListProps> = ({
{allPlaylists.map((playlist) => ( {allPlaylists.map((playlist) => (
<MenuItem <MenuItem
key={playlist.id} key={playlist.id}
onClick={() => { onClick={(e) => {
handleBulkAddToPlaylist(playlist.name); e.stopPropagation();
onAddToPlaylist([Array.from(selectedSongs)[0]], playlist.name);
}} }}
> >
Add to {playlist.name} Add to {playlist.name}
@ -159,9 +162,9 @@ export const SongList: React.FC<SongListProps> = ({
<MenuDivider /> <MenuDivider />
<MenuItem <MenuItem
color="red.300" color="red.300"
onClick={() => { onClick={(e) => {
e.stopPropagation();
onRemoveFromPlaylist(Array.from(selectedSongs)); onRemoveFromPlaylist(Array.from(selectedSongs));
setSelectedSongs(new Set());
}} }}
> >
Remove from {currentPlaylist} Remove from {currentPlaylist}
@ -180,6 +183,7 @@ export const SongList: React.FC<SongListProps> = ({
key={song.id} key={song.id}
alignItems="center" alignItems="center"
p={2} p={2}
pl={depth > 0 ? 4 + (depth * 4) : 2}
borderBottom="1px" borderBottom="1px"
borderColor="gray.700" borderColor="gray.700"
bg={selectedSongId === song.id ? "gray.700" : "transparent"} bg={selectedSongId === song.id ? "gray.700" : "transparent"}
@ -194,12 +198,20 @@ export const SongList: React.FC<SongListProps> = ({
}} }}
mr={4} mr={4}
onClick={(e) => e.stopPropagation()} onClick={(e) => e.stopPropagation()}
size={depth > 0 ? "sm" : "md"}
/> />
<Box flex="1"> <Box flex="1">
<Text fontWeight="bold" color={selectedSongId === song.id ? "white" : "gray.100"}> <Text
fontWeight="bold"
color={selectedSongId === song.id ? "white" : "gray.100"}
fontSize={depth > 0 ? "sm" : "md"}
>
{song.title} {song.title}
</Text> </Text>
<Text fontSize="sm" color={selectedSongId === song.id ? "gray.300" : "gray.500"}> <Text
fontSize={depth > 0 ? "xs" : "sm"}
color={selectedSongId === song.id ? "gray.300" : "gray.500"}
>
{song.artist} {song.artist}
</Text> </Text>
</Box> </Box>
@ -210,6 +222,8 @@ export const SongList: React.FC<SongListProps> = ({
icon={<ChevronDownIcon />} icon={<ChevronDownIcon />}
variant="ghost" variant="ghost"
onClick={(e) => e.stopPropagation()} onClick={(e) => e.stopPropagation()}
size={depth > 0 ? "xs" : "sm"}
ml="auto"
/> />
<MenuList> <MenuList>
{allPlaylists.map((playlist) => ( {allPlaylists.map((playlist) => (