Compare commits
No commits in common. "f82cb84397946564c293b947c4cb8c41a2ae43ed" and "8daf0cd526da004b828c32d711283a3bfe4d124d" have entirely different histories.
f82cb84397
...
8daf0cd526
@ -1,19 +1,7 @@
|
|||||||
* {
|
|
||||||
margin: 0;
|
|
||||||
padding: 0;
|
|
||||||
box-sizing: border-box;
|
|
||||||
}
|
|
||||||
|
|
||||||
html, body, #root {
|
|
||||||
width: 100%;
|
|
||||||
height: 100%;
|
|
||||||
margin: 0;
|
|
||||||
padding: 0;
|
|
||||||
overflow: hidden;
|
|
||||||
}
|
|
||||||
|
|
||||||
#root {
|
#root {
|
||||||
max-width: 1280px;
|
max-width: 1280px;
|
||||||
|
margin: 0;
|
||||||
|
padding: 1rem;
|
||||||
}
|
}
|
||||||
|
|
||||||
.logo {
|
.logo {
|
||||||
|
|||||||
@ -1,5 +1,4 @@
|
|||||||
import { Box, Button, Flex, Heading, Input, Spinner, Text, useStyleConfig, useBreakpointValue, IconButton, Drawer, DrawerBody, DrawerHeader, DrawerOverlay, DrawerContent, DrawerCloseButton, useDisclosure, Container, Menu, MenuButton, MenuList, MenuItem, useToken } from "@chakra-ui/react";
|
import { Box, Button, Flex, Heading, Input, Spinner, Text, useStyleConfig } from "@chakra-ui/react";
|
||||||
import { ChevronLeftIcon, ChevronRightIcon, HamburgerIcon, ViewIcon, SettingsIcon, DragHandleIcon } from "@chakra-ui/icons";
|
|
||||||
import { useState, useRef, useEffect } from "react";
|
import { useState, useRef, useEffect } from "react";
|
||||||
import { useNavigate, useLocation } from "react-router-dom";
|
import { useNavigate, useLocation } from "react-router-dom";
|
||||||
import { SongList } from "./components/SongList";
|
import { SongList } from "./components/SongList";
|
||||||
@ -11,7 +10,7 @@ import { api } from "./services/api";
|
|||||||
import { Song } from "./types/interfaces";
|
import { Song } from "./types/interfaces";
|
||||||
import "./App.css";
|
import "./App.css";
|
||||||
|
|
||||||
const StyledFileInput = ({ isMobile = false }) => {
|
const StyledFileInput = () => {
|
||||||
const { handleFileUpload } = useXmlParser();
|
const { handleFileUpload } = useXmlParser();
|
||||||
const inputRef = useRef<HTMLInputElement>(null);
|
const inputRef = useRef<HTMLInputElement>(null);
|
||||||
|
|
||||||
@ -23,7 +22,7 @@ const StyledFileInput = ({ isMobile = false }) => {
|
|||||||
<Box
|
<Box
|
||||||
position="relative"
|
position="relative"
|
||||||
width="auto"
|
width="auto"
|
||||||
maxW={isMobile ? "100%" : "300px"}
|
maxW="300px"
|
||||||
>
|
>
|
||||||
<Input
|
<Input
|
||||||
type="file"
|
type="file"
|
||||||
@ -55,7 +54,6 @@ const StyledFileInput = ({ isMobile = false }) => {
|
|||||||
bg: "gray.500"
|
bg: "gray.500"
|
||||||
}}
|
}}
|
||||||
onClick={handleClick}
|
onClick={handleClick}
|
||||||
size={isMobile ? "sm" : "md"}
|
|
||||||
>
|
>
|
||||||
Choose XML File
|
Choose XML File
|
||||||
</Button>
|
</Button>
|
||||||
@ -63,48 +61,12 @@ const StyledFileInput = ({ isMobile = false }) => {
|
|||||||
);
|
);
|
||||||
};
|
};
|
||||||
|
|
||||||
const ResizeHandle = ({ onMouseDown }: { onMouseDown: (e: React.MouseEvent) => void }) => (
|
|
||||||
<Box
|
|
||||||
position="absolute"
|
|
||||||
right="-4px"
|
|
||||||
top={0}
|
|
||||||
bottom={0}
|
|
||||||
width="8px"
|
|
||||||
cursor="col-resize"
|
|
||||||
zIndex={1}
|
|
||||||
_hover={{
|
|
||||||
'&::after': {
|
|
||||||
bg: 'blue.500',
|
|
||||||
}
|
|
||||||
}}
|
|
||||||
onMouseDown={onMouseDown}
|
|
||||||
>
|
|
||||||
<Box
|
|
||||||
position="absolute"
|
|
||||||
left="3px"
|
|
||||||
top={0}
|
|
||||||
bottom={0}
|
|
||||||
width="2px"
|
|
||||||
bg="gray.600"
|
|
||||||
transition="background-color 0.2s"
|
|
||||||
_hover={{ bg: 'blue.500' }}
|
|
||||||
/>
|
|
||||||
</Box>
|
|
||||||
);
|
|
||||||
|
|
||||||
export default function RekordboxReader() {
|
export default function RekordboxReader() {
|
||||||
const { songs, playlists, setPlaylists, loading } = useXmlParser();
|
const { songs, playlists, setPlaylists, loading } = useXmlParser();
|
||||||
const [selectedSong, setSelectedSong] = useState<Song | null>(null);
|
const [selectedSong, setSelectedSong] = useState<Song | null>(null);
|
||||||
const navigate = useNavigate();
|
const navigate = useNavigate();
|
||||||
const location = useLocation();
|
const location = useLocation();
|
||||||
const initialLoadDone = useRef(false);
|
const initialLoadDone = useRef(false);
|
||||||
const mobileFileInputRef = useRef<HTMLInputElement>(null);
|
|
||||||
const { isOpen, onOpen, onClose } = useDisclosure();
|
|
||||||
|
|
||||||
const isMobile = useBreakpointValue({ base: true, md: false });
|
|
||||||
const [sidebarWidth, setSidebarWidth] = useState(400);
|
|
||||||
const [isResizing, setIsResizing] = useState(false);
|
|
||||||
const resizeRef = useRef<{ startX: number; startWidth: number } | null>(null);
|
|
||||||
|
|
||||||
// Get the current playlist from URL or default to "All Songs"
|
// Get the current playlist from URL or default to "All Songs"
|
||||||
const currentPlaylist = location.pathname === "/"
|
const currentPlaylist = location.pathname === "/"
|
||||||
@ -178,54 +140,12 @@ export default function RekordboxReader() {
|
|||||||
document.body.removeChild(a);
|
document.body.removeChild(a);
|
||||||
};
|
};
|
||||||
|
|
||||||
const handlePlaylistDelete = async (name: string) => {
|
|
||||||
const updatedPlaylists = playlists.filter(p => p.name !== name);
|
|
||||||
const savedPlaylists = await api.savePlaylists(updatedPlaylists);
|
|
||||||
setPlaylists(savedPlaylists);
|
|
||||||
if (currentPlaylist === name) {
|
|
||||||
navigate("/"); // Navigate back to All Songs if the current playlist is deleted
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
const displayedSongs = currentPlaylist === "All Songs"
|
const displayedSongs = currentPlaylist === "All Songs"
|
||||||
? songs
|
? songs
|
||||||
: songs.filter((song) =>
|
: songs.filter((song) =>
|
||||||
playlists.find((p) => p.name === currentPlaylist)?.tracks.includes(song.id)
|
playlists.find((p) => p.name === currentPlaylist)?.tracks.includes(song.id)
|
||||||
);
|
);
|
||||||
|
|
||||||
const handleResizeStart = (e: React.MouseEvent) => {
|
|
||||||
e.preventDefault();
|
|
||||||
setIsResizing(true);
|
|
||||||
resizeRef.current = {
|
|
||||||
startX: e.pageX,
|
|
||||||
startWidth: sidebarWidth,
|
|
||||||
};
|
|
||||||
};
|
|
||||||
|
|
||||||
const handleResizeMove = (e: MouseEvent) => {
|
|
||||||
if (!isResizing || !resizeRef.current) return;
|
|
||||||
|
|
||||||
const delta = e.pageX - resizeRef.current.startX;
|
|
||||||
const newWidth = Math.max(300, Math.min(800, resizeRef.current.startWidth + delta));
|
|
||||||
setSidebarWidth(newWidth);
|
|
||||||
};
|
|
||||||
|
|
||||||
const handleResizeEnd = () => {
|
|
||||||
setIsResizing(false);
|
|
||||||
resizeRef.current = null;
|
|
||||||
};
|
|
||||||
|
|
||||||
useEffect(() => {
|
|
||||||
if (isResizing) {
|
|
||||||
window.addEventListener('mousemove', handleResizeMove);
|
|
||||||
window.addEventListener('mouseup', handleResizeEnd);
|
|
||||||
}
|
|
||||||
return () => {
|
|
||||||
window.removeEventListener('mousemove', handleResizeMove);
|
|
||||||
window.removeEventListener('mouseup', handleResizeEnd);
|
|
||||||
};
|
|
||||||
}, [isResizing]);
|
|
||||||
|
|
||||||
if (loading) {
|
if (loading) {
|
||||||
return (
|
return (
|
||||||
<Flex height="100vh" align="center" justify="center" direction="column" gap={4}>
|
<Flex height="100vh" align="center" justify="center" direction="column" gap={4}>
|
||||||
@ -240,59 +160,11 @@ export default function RekordboxReader() {
|
|||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
const playlistManager = (
|
|
||||||
<PlaylistManager
|
|
||||||
playlists={playlists}
|
|
||||||
selectedItem={currentPlaylist}
|
|
||||||
onPlaylistCreate={handleCreatePlaylist}
|
|
||||||
onPlaylistSelect={(name) => {
|
|
||||||
handlePlaylistSelect(name || "All Songs");
|
|
||||||
if (isMobile) onClose();
|
|
||||||
}}
|
|
||||||
onPlaylistDelete={handlePlaylistDelete}
|
|
||||||
/>
|
|
||||||
);
|
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<Box
|
<Box>
|
||||||
position="fixed"
|
<Flex direction="column" gap={4} mb={6}>
|
||||||
top={0}
|
<Heading size="md" mb={2}>Rekordbox Reader</Heading>
|
||||||
left={0}
|
<Flex gap={4} align="center">
|
||||||
right={0}
|
|
||||||
bottom={0}
|
|
||||||
overflow="hidden"
|
|
||||||
margin={0}
|
|
||||||
padding={0}
|
|
||||||
userSelect={isResizing ? 'none' : 'auto'}
|
|
||||||
>
|
|
||||||
<Flex direction="column" h="100%" w="100%">
|
|
||||||
{/* Header */}
|
|
||||||
<Flex
|
|
||||||
px={isMobile ? 2 : 4}
|
|
||||||
py={2}
|
|
||||||
bg="gray.800"
|
|
||||||
borderBottom="1px"
|
|
||||||
borderColor="gray.700"
|
|
||||||
align="center"
|
|
||||||
gap={2}
|
|
||||||
w="full"
|
|
||||||
>
|
|
||||||
{isMobile && (
|
|
||||||
<IconButton
|
|
||||||
aria-label="Open menu"
|
|
||||||
icon={<ChevronRightIcon />}
|
|
||||||
onClick={onOpen}
|
|
||||||
variant="solid"
|
|
||||||
colorScheme="blue"
|
|
||||||
size="md"
|
|
||||||
fontSize="20px"
|
|
||||||
/>
|
|
||||||
)}
|
|
||||||
<Heading size={isMobile ? "sm" : "md"}>Rekordbox Reader</Heading>
|
|
||||||
|
|
||||||
{/* Desktop Actions */}
|
|
||||||
{!isMobile && (
|
|
||||||
<Flex gap={2} align="center" ml="auto">
|
|
||||||
<StyledFileInput />
|
<StyledFileInput />
|
||||||
{songs.length > 0 && (
|
{songs.length > 0 && (
|
||||||
<Button onClick={handleExport} size="sm" width="auto">
|
<Button onClick={handleExport} size="sm" width="auto">
|
||||||
@ -300,105 +172,17 @@ export default function RekordboxReader() {
|
|||||||
</Button>
|
</Button>
|
||||||
)}
|
)}
|
||||||
</Flex>
|
</Flex>
|
||||||
)}
|
|
||||||
|
|
||||||
{/* Mobile Actions */}
|
|
||||||
{isMobile && (
|
|
||||||
<Menu>
|
|
||||||
<MenuButton
|
|
||||||
as={IconButton}
|
|
||||||
icon={<SettingsIcon />}
|
|
||||||
variant="ghost"
|
|
||||||
ml="auto"
|
|
||||||
size="md"
|
|
||||||
color="gray.300"
|
|
||||||
_hover={{ color: "white", bg: "whiteAlpha.200" }}
|
|
||||||
/>
|
|
||||||
<MenuList bg="gray.800" borderColor="gray.700">
|
|
||||||
<MenuItem
|
|
||||||
bg="gray.800"
|
|
||||||
_hover={{ bg: "gray.700" }}
|
|
||||||
>
|
|
||||||
<StyledFileInput isMobile />
|
|
||||||
</MenuItem>
|
|
||||||
{songs.length > 0 && (
|
|
||||||
<MenuItem
|
|
||||||
bg="gray.800"
|
|
||||||
_hover={{ bg: "gray.700" }}
|
|
||||||
onClick={handleExport}
|
|
||||||
color="gray.100"
|
|
||||||
>
|
|
||||||
Export XML
|
|
||||||
</MenuItem>
|
|
||||||
)}
|
|
||||||
</MenuList>
|
|
||||||
</Menu>
|
|
||||||
)}
|
|
||||||
</Flex>
|
</Flex>
|
||||||
|
<Flex gap={4} alignItems="start">
|
||||||
{/* Main Content */}
|
<Box w="200px">
|
||||||
<Flex flex={1} overflow="hidden" w="full">
|
<PlaylistManager
|
||||||
{/* Sidebar - Desktop */}
|
playlists={playlists}
|
||||||
{!isMobile && (
|
selectedItem={currentPlaylist}
|
||||||
<Box
|
onPlaylistCreate={handleCreatePlaylist}
|
||||||
position="relative"
|
onPlaylistSelect={handlePlaylistSelect}
|
||||||
w={`${sidebarWidth}px`}
|
|
||||||
minW={`${sidebarWidth}px`}
|
|
||||||
p={4}
|
|
||||||
borderRight="1px"
|
|
||||||
borderColor="gray.700"
|
|
||||||
overflowY="auto"
|
|
||||||
bg="gray.900"
|
|
||||||
>
|
|
||||||
{playlistManager}
|
|
||||||
<ResizeHandle onMouseDown={handleResizeStart} />
|
|
||||||
</Box>
|
|
||||||
)}
|
|
||||||
|
|
||||||
{/* Sidebar - Mobile */}
|
|
||||||
<Drawer
|
|
||||||
isOpen={isOpen}
|
|
||||||
placement="left"
|
|
||||||
onClose={onClose}
|
|
||||||
size="full"
|
|
||||||
>
|
|
||||||
<DrawerOverlay />
|
|
||||||
<DrawerContent bg="gray.900" p={0}>
|
|
||||||
<DrawerHeader
|
|
||||||
borderBottomWidth="1px"
|
|
||||||
bg="gray.800"
|
|
||||||
display="flex"
|
|
||||||
alignItems="center"
|
|
||||||
px={2}
|
|
||||||
py={2}
|
|
||||||
>
|
|
||||||
<Text>Playlists</Text>
|
|
||||||
<IconButton
|
|
||||||
aria-label="Close menu"
|
|
||||||
icon={<ChevronLeftIcon />}
|
|
||||||
onClick={onClose}
|
|
||||||
variant="ghost"
|
|
||||||
ml="auto"
|
|
||||||
color="blue.400"
|
|
||||||
_hover={{ color: "blue.300", bg: "whiteAlpha.200" }}
|
|
||||||
/>
|
/>
|
||||||
</DrawerHeader>
|
</Box>
|
||||||
<DrawerBody p={2}>
|
<Box flex={1}>
|
||||||
{playlistManager}
|
|
||||||
</DrawerBody>
|
|
||||||
</DrawerContent>
|
|
||||||
</Drawer>
|
|
||||||
|
|
||||||
{/* Main Content Area */}
|
|
||||||
<Flex
|
|
||||||
flex={1}
|
|
||||||
gap={4}
|
|
||||||
p={isMobile ? 2 : 4}
|
|
||||||
overflowY="hidden"
|
|
||||||
w="full"
|
|
||||||
>
|
|
||||||
{/* Song List */}
|
|
||||||
<Box flex={1} overflowY="auto" w="full">
|
|
||||||
<SongList
|
<SongList
|
||||||
songs={displayedSongs}
|
songs={displayedSongs}
|
||||||
onAddToPlaylist={handleAddSongsToPlaylist}
|
onAddToPlaylist={handleAddSongsToPlaylist}
|
||||||
@ -409,36 +193,7 @@ export default function RekordboxReader() {
|
|||||||
currentPlaylist={currentPlaylist}
|
currentPlaylist={currentPlaylist}
|
||||||
/>
|
/>
|
||||||
</Box>
|
</Box>
|
||||||
|
|
||||||
{/* Details Panel */}
|
|
||||||
{!isMobile && (
|
|
||||||
<Box
|
|
||||||
w="350px"
|
|
||||||
minW="350px"
|
|
||||||
p={4}
|
|
||||||
borderLeft="1px"
|
|
||||||
borderColor="gray.700"
|
|
||||||
overflowY="auto"
|
|
||||||
bg="gray.900"
|
|
||||||
sx={{
|
|
||||||
'&::-webkit-scrollbar': {
|
|
||||||
width: '8px',
|
|
||||||
borderRadius: '8px',
|
|
||||||
backgroundColor: 'gray.900',
|
|
||||||
},
|
|
||||||
'&::-webkit-scrollbar-thumb': {
|
|
||||||
backgroundColor: 'gray.700',
|
|
||||||
borderRadius: '8px',
|
|
||||||
},
|
|
||||||
overflowY: 'auto',
|
|
||||||
overflowX: 'hidden',
|
|
||||||
}}
|
|
||||||
>
|
|
||||||
<SongDetails song={selectedSong} />
|
<SongDetails song={selectedSong} />
|
||||||
</Box>
|
|
||||||
)}
|
|
||||||
</Flex>
|
|
||||||
</Flex>
|
|
||||||
</Flex>
|
</Flex>
|
||||||
</Box>
|
</Box>
|
||||||
);
|
);
|
||||||
|
|||||||
@ -13,13 +13,7 @@ import {
|
|||||||
Text,
|
Text,
|
||||||
useDisclosure,
|
useDisclosure,
|
||||||
VStack,
|
VStack,
|
||||||
Menu,
|
|
||||||
MenuButton,
|
|
||||||
MenuList,
|
|
||||||
MenuItem,
|
|
||||||
IconButton,
|
|
||||||
} from "@chakra-ui/react";
|
} from "@chakra-ui/react";
|
||||||
import { ChevronDownIcon, DeleteIcon } from "@chakra-ui/icons";
|
|
||||||
import React, { useState } from "react";
|
import React, { useState } from "react";
|
||||||
import { Playlist } from "../types/Playlist";
|
import { Playlist } from "../types/Playlist";
|
||||||
|
|
||||||
@ -27,8 +21,7 @@ interface PlaylistManagerProps {
|
|||||||
playlists: Playlist[];
|
playlists: Playlist[];
|
||||||
selectedItem: string | null;
|
selectedItem: string | null;
|
||||||
onPlaylistCreate: (name: string) => void;
|
onPlaylistCreate: (name: string) => void;
|
||||||
onPlaylistSelect: (name: string | null) => void;
|
onPlaylistSelect: (id: string | null) => void;
|
||||||
onPlaylistDelete: (name: string) => void;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
const getButtonStyles = (isSelected: boolean) => ({
|
const getButtonStyles = (isSelected: boolean) => ({
|
||||||
@ -55,7 +48,6 @@ export const PlaylistManager: React.FC<PlaylistManagerProps> = ({
|
|||||||
selectedItem,
|
selectedItem,
|
||||||
onPlaylistCreate,
|
onPlaylistCreate,
|
||||||
onPlaylistSelect,
|
onPlaylistSelect,
|
||||||
onPlaylistDelete,
|
|
||||||
}) => {
|
}) => {
|
||||||
const { isOpen, onOpen, onClose } = useDisclosure();
|
const { isOpen, onOpen, onClose } = useDisclosure();
|
||||||
const [newPlaylistName, setNewPlaylistName] = useState("");
|
const [newPlaylistName, setNewPlaylistName] = useState("");
|
||||||
@ -78,37 +70,13 @@ export const PlaylistManager: React.FC<PlaylistManagerProps> = ({
|
|||||||
All Songs
|
All Songs
|
||||||
</Button>
|
</Button>
|
||||||
{playlists.map((playlist) => (
|
{playlists.map((playlist) => (
|
||||||
<Flex key={playlist._id} align="center" gap={1}>
|
|
||||||
<Button
|
<Button
|
||||||
flex={1}
|
key={playlist._id}
|
||||||
{...getButtonStyles(selectedItem === playlist.name)}
|
{...getButtonStyles(selectedItem === playlist.name)}
|
||||||
onClick={() => onPlaylistSelect(playlist.name)}
|
onClick={() => onPlaylistSelect(playlist.name)}
|
||||||
>
|
>
|
||||||
{playlist.name}
|
{playlist.name}
|
||||||
</Button>
|
</Button>
|
||||||
<Menu>
|
|
||||||
<MenuButton
|
|
||||||
as={IconButton}
|
|
||||||
aria-label="Playlist options"
|
|
||||||
icon={<ChevronDownIcon />}
|
|
||||||
variant="ghost"
|
|
||||||
size="sm"
|
|
||||||
color="gray.400"
|
|
||||||
_hover={{ color: "white", bg: "whiteAlpha.200" }}
|
|
||||||
/>
|
|
||||||
<MenuList bg="gray.800" borderColor="gray.700">
|
|
||||||
<MenuItem
|
|
||||||
bg="gray.800"
|
|
||||||
color="red.300"
|
|
||||||
_hover={{ bg: "gray.700" }}
|
|
||||||
icon={<DeleteIcon />}
|
|
||||||
onClick={() => onPlaylistDelete(playlist.name)}
|
|
||||||
>
|
|
||||||
Delete Playlist
|
|
||||||
</MenuItem>
|
|
||||||
</MenuList>
|
|
||||||
</Menu>
|
|
||||||
</Flex>
|
|
||||||
))}
|
))}
|
||||||
</VStack>
|
</VStack>
|
||||||
|
|
||||||
|
|||||||
@ -8,7 +8,17 @@ interface SongDetailsProps {
|
|||||||
export const SongDetails: React.FC<SongDetailsProps> = ({ song }) => {
|
export const SongDetails: React.FC<SongDetailsProps> = ({ song }) => {
|
||||||
if (!song) {
|
if (!song) {
|
||||||
return (
|
return (
|
||||||
<Box h="full" p={4}>
|
<Box
|
||||||
|
w="300px"
|
||||||
|
position="sticky"
|
||||||
|
top={4}
|
||||||
|
h="calc(100vh - 2rem)"
|
||||||
|
bg="gray.800"
|
||||||
|
p={4}
|
||||||
|
borderRadius="md"
|
||||||
|
borderLeft="1px"
|
||||||
|
borderColor="gray.700"
|
||||||
|
>
|
||||||
<Text color="gray.500">Select a song to view details</Text>
|
<Text color="gray.500">Select a song to view details</Text>
|
||||||
</Box>
|
</Box>
|
||||||
);
|
);
|
||||||
@ -26,11 +36,37 @@ export const SongDetails: React.FC<SongDetailsProps> = ({ song }) => {
|
|||||||
{ label: "Mix", value: song.mix },
|
{ label: "Mix", value: song.mix },
|
||||||
{ label: "Rating", value: song.rating },
|
{ label: "Rating", value: song.rating },
|
||||||
{ label: "Comments", value: song.comments },
|
{ label: "Comments", value: song.comments },
|
||||||
].filter(detail => detail.value);
|
].filter(detail => detail.value); // Only show fields that have values
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<Box h="full">
|
<Box
|
||||||
<VStack align="stretch" spacing={4} p={4}>
|
w="300px"
|
||||||
|
position="sticky"
|
||||||
|
top={4}
|
||||||
|
h="calc(100vh - 2rem)"
|
||||||
|
bg="gray.800"
|
||||||
|
borderRadius="md"
|
||||||
|
borderLeft="1px"
|
||||||
|
borderColor="gray.700"
|
||||||
|
display="flex"
|
||||||
|
flexDirection="column"
|
||||||
|
>
|
||||||
|
<Box p={4} flex="1" overflowY="auto" css={{
|
||||||
|
'&::-webkit-scrollbar': {
|
||||||
|
width: '4px',
|
||||||
|
},
|
||||||
|
'&::-webkit-scrollbar-track': {
|
||||||
|
background: 'transparent',
|
||||||
|
},
|
||||||
|
'&::-webkit-scrollbar-thumb': {
|
||||||
|
background: 'var(--chakra-colors-gray-600)',
|
||||||
|
borderRadius: '2px',
|
||||||
|
},
|
||||||
|
'&::-webkit-scrollbar-thumb:hover': {
|
||||||
|
background: 'var(--chakra-colors-gray-500)',
|
||||||
|
},
|
||||||
|
}}>
|
||||||
|
<VStack align="stretch" spacing={4}>
|
||||||
<Box>
|
<Box>
|
||||||
<Text fontSize="lg" fontWeight="bold" color="white">
|
<Text fontSize="lg" fontWeight="bold" color="white">
|
||||||
{song.title}
|
{song.title}
|
||||||
@ -78,5 +114,6 @@ export const SongDetails: React.FC<SongDetailsProps> = ({ song }) => {
|
|||||||
)}
|
)}
|
||||||
</VStack>
|
</VStack>
|
||||||
</Box>
|
</Box>
|
||||||
|
</Box>
|
||||||
);
|
);
|
||||||
};
|
};
|
||||||
Loading…
x
Reference in New Issue
Block a user