UI fixes!
This commit is contained in:
parent
1a3beb7e6f
commit
7e1f4e1cd4
@ -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
|
||||||
|
|||||||
@ -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) => (
|
||||||
|
|||||||
Loading…
x
Reference in New Issue
Block a user