From f82cb84397946564c293b947c4cb8c41a2ae43ed Mon Sep 17 00:00:00 2001 From: Geert Rademakes Date: Thu, 24 Apr 2025 23:11:46 +0200 Subject: [PATCH] Draggable playlist sidebar --- packages/frontend/src/App.tsx | 76 +++++++++++++++++++++++++++++++++-- 1 file changed, 72 insertions(+), 4 deletions(-) diff --git a/packages/frontend/src/App.tsx b/packages/frontend/src/App.tsx index a05aab9..c5ab944 100644 --- a/packages/frontend/src/App.tsx +++ b/packages/frontend/src/App.tsx @@ -1,5 +1,5 @@ -import { Box, Button, Flex, Heading, Input, Spinner, Text, useStyleConfig, useBreakpointValue, IconButton, Drawer, DrawerBody, DrawerHeader, DrawerOverlay, DrawerContent, DrawerCloseButton, useDisclosure, Container, Menu, MenuButton, MenuList, MenuItem } from "@chakra-ui/react"; -import { ChevronLeftIcon, ChevronRightIcon, HamburgerIcon, ViewIcon, SettingsIcon } from "@chakra-ui/icons"; +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 { ChevronLeftIcon, ChevronRightIcon, HamburgerIcon, ViewIcon, SettingsIcon, DragHandleIcon } from "@chakra-ui/icons"; import { useState, useRef, useEffect } from "react"; import { useNavigate, useLocation } from "react-router-dom"; import { SongList } from "./components/SongList"; @@ -63,6 +63,35 @@ const StyledFileInput = ({ isMobile = false }) => { ); }; +const ResizeHandle = ({ onMouseDown }: { onMouseDown: (e: React.MouseEvent) => void }) => ( + + + +); + export default function RekordboxReader() { const { songs, playlists, setPlaylists, loading } = useXmlParser(); const [selectedSong, setSelectedSong] = useState(null); @@ -73,6 +102,9 @@ export default function RekordboxReader() { 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" const currentPlaylist = location.pathname === "/" @@ -161,6 +193,39 @@ export default function RekordboxReader() { 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) { return ( @@ -198,6 +263,7 @@ export default function RekordboxReader() { overflow="hidden" margin={0} padding={0} + userSelect={isResizing ? 'none' : 'auto'} > {/* Header */} @@ -275,8 +341,9 @@ export default function RekordboxReader() { {/* Sidebar - Desktop */} {!isMobile && ( {playlistManager} + )}