diff --git a/src/renderer/features/shared/components/resize-handle.tsx b/src/renderer/features/shared/components/resize-handle.tsx
new file mode 100644
index 00000000..de1cd6fc
--- /dev/null
+++ b/src/renderer/features/shared/components/resize-handle.tsx
@@ -0,0 +1,33 @@
+import styled from 'styled-components';
+
+export const ResizeHandle = styled.div<{
+ isResizing: boolean;
+ placement: 'top' | 'left' | 'bottom' | 'right';
+}>`
+ position: absolute;
+ top: ${(props) => props.placement === 'top' && 0};
+ right: ${(props) => props.placement === 'right' && 0};
+ bottom: ${(props) => props.placement === 'bottom' && 0};
+ left: ${(props) => props.placement === 'left' && 0};
+ z-index: 90;
+ width: 4px;
+ height: 100%;
+ cursor: ew-resize;
+ opacity: ${(props) => (props.isResizing ? 1 : 0)};
+
+ &:hover {
+ opacity: 0.7;
+ }
+
+ &::before {
+ position: absolute;
+ top: ${(props) => props.placement === 'top' && 0};
+ right: ${(props) => props.placement === 'right' && 0};
+ bottom: ${(props) => props.placement === 'bottom' && 0};
+ left: ${(props) => props.placement === 'left' && 0};
+ width: 1px;
+ height: 100%;
+ background-color: var(--sidebar-handle-bg);
+ content: '';
+ }
+`;
diff --git a/src/renderer/features/shared/index.ts b/src/renderer/features/shared/index.ts
index eaf5dc01..93f39eaa 100644
--- a/src/renderer/features/shared/index.ts
+++ b/src/renderer/features/shared/index.ts
@@ -8,3 +8,4 @@ export * from './mutations/create-favorite-mutation';
export * from './mutations/delete-favorite-mutation';
export * from './mutations/set-rating-mutation';
export * from './components/filter-bar';
+export * from './components/resize-handle';
diff --git a/src/renderer/layouts/default-layout/full-screen-overlay.tsx b/src/renderer/layouts/default-layout/full-screen-overlay.tsx
new file mode 100644
index 00000000..85899947
--- /dev/null
+++ b/src/renderer/layouts/default-layout/full-screen-overlay.tsx
@@ -0,0 +1,18 @@
+import { AnimatePresence } from 'framer-motion';
+import { FullScreenPlayer } from '/@/renderer/features/player/components/full-screen-player';
+import { useFullScreenPlayerStore } from '/@/renderer/store';
+
+export const FullScreenOverlay = () => {
+ const { expanded: isFullScreenPlayerExpanded } = useFullScreenPlayerStore();
+
+ return (
+ <>
+
+ {isFullScreenPlayerExpanded && }
+
+ >
+ );
+};
diff --git a/src/renderer/layouts/default-layout/left-sidebar.tsx b/src/renderer/layouts/default-layout/left-sidebar.tsx
new file mode 100644
index 00000000..d84b1241
--- /dev/null
+++ b/src/renderer/layouts/default-layout/left-sidebar.tsx
@@ -0,0 +1,38 @@
+import { useRef } from 'react';
+import styled from 'styled-components';
+import { ResizeHandle } from '/@/renderer/features/shared';
+import { CollapsedSidebar } from '/@/renderer/features/sidebar/components/collapsed-sidebar';
+import { Sidebar } from '/@/renderer/features/sidebar/components/sidebar';
+import { useSidebarStore } from '/@/renderer/store';
+
+const SidebarContainer = styled.aside`
+ position: relative;
+ grid-area: sidebar;
+ background: var(--sidebar-bg);
+ border-right: var(--sidebar-border);
+`;
+
+interface LeftSidebarProps {
+ isResizing: boolean;
+ startResizing: (direction: 'left' | 'right') => void;
+}
+
+export const LeftSidebar = ({ isResizing, startResizing }: LeftSidebarProps) => {
+ const sidebarRef = useRef(null);
+ const { collapsed } = useSidebarStore();
+
+ return (
+
+ );
+};
diff --git a/src/renderer/layouts/default-layout/main-content.tsx b/src/renderer/layouts/default-layout/main-content.tsx
index 6acd1233..39f30ada 100644
--- a/src/renderer/layouts/default-layout/main-content.tsx
+++ b/src/renderer/layouts/default-layout/main-content.tsx
@@ -1,19 +1,20 @@
-import { useDisclosure, useTimeout } from '@mantine/hooks';
-import { motion, AnimatePresence, Variants } from 'framer-motion';
+import { lazy, Suspense, useCallback, useEffect, useMemo, useRef, useState } from 'react';
import { throttle } from 'lodash';
-import { Suspense, useCallback, useEffect, useMemo, useRef, useState } from 'react';
-import { TbArrowBarLeft } from 'react-icons/tb';
import { Outlet, useLocation } from 'react-router';
import styled from 'styled-components';
-import { DrawerPlayQueue, SidebarPlayQueue } from '/@/renderer/features/now-playing';
-import { FullScreenPlayer } from '/@/renderer/features/player/components/full-screen-player';
-import { Sidebar } from '/@/renderer/features/sidebar/components/sidebar';
-import { CollapsedSidebar } from '../../features/sidebar/components/collapsed-sidebar';
import { AppRoute } from '/@/renderer/router/routes';
-import { useAppStore, useAppStoreActions, useFullScreenPlayerStore } from '/@/renderer/store';
-import { useWindowSettings, useGeneralSettings } from '/@/renderer/store/settings.store';
-import { Platform } from '/@/renderer/types';
+import { useAppStoreActions, useSidebarStore } from '/@/renderer/store';
+import { useGeneralSettings } from '/@/renderer/store/settings.store';
import { constrainSidebarWidth, constrainRightSidebarWidth } from '/@/renderer/utils';
+import { LeftSidebar } from '/@/renderer/layouts/default-layout/left-sidebar';
+import { FullScreenOverlay } from '/@/renderer/layouts/default-layout/full-screen-overlay';
+import { RightSidebar } from '/@/renderer/layouts/default-layout/right-sidebar';
+
+const SideDrawerQueue = lazy(() =>
+ import('/@/renderer/layouts/default-layout/side-drawer-queue').then((module) => ({
+ default: module.SideDrawerQueue,
+ })),
+);
const MINIMUM_SIDEBAR_WIDTH = 260;
@@ -37,171 +38,17 @@ const MainContentContainer = styled.div<{
background: var(--main-bg);
`;
-const SidebarContainer = styled.aside`
- position: relative;
- grid-area: sidebar;
- background: var(--sidebar-bg);
- border-right: var(--sidebar-border);
-`;
-
-const RightSidebarContainer = styled(motion.aside)`
- position: relative;
- grid-area: right-sidebar;
- height: 100%;
- background: var(--sidebar-bg);
- border-left: var(--sidebar-border);
-`;
-
-const ResizeHandle = styled.div<{
- isResizing: boolean;
- placement: 'top' | 'left' | 'bottom' | 'right';
-}>`
- position: absolute;
- top: ${(props) => props.placement === 'top' && 0};
- right: ${(props) => props.placement === 'right' && 0};
- bottom: ${(props) => props.placement === 'bottom' && 0};
- left: ${(props) => props.placement === 'left' && 0};
- z-index: 90;
- width: 4px;
- height: 100%;
- cursor: ew-resize;
- opacity: ${(props) => (props.isResizing ? 1 : 0)};
-
- &:hover {
- opacity: 0.7;
- }
-
- &::before {
- position: absolute;
- top: ${(props) => props.placement === 'top' && 0};
- right: ${(props) => props.placement === 'right' && 0};
- bottom: ${(props) => props.placement === 'bottom' && 0};
- left: ${(props) => props.placement === 'left' && 0};
- width: 1px;
- height: 100%;
- background-color: var(--sidebar-handle-bg);
- content: '';
- }
-`;
-
-const QueueDrawer = styled(motion.div)`
- background: var(--main-bg);
- border: 3px solid var(--generic-border-color);
- border-radius: 10px;
-`;
-
-const QueueDrawerArea = styled(motion.div)`
- position: absolute;
- top: 50%;
- right: 25px;
- z-index: 100;
- display: flex;
- align-items: center;
- width: 20px;
- height: 30px;
- user-select: none;
-`;
-
-const queueDrawerVariants: Variants = {
- closed: (windowBarStyle) => ({
- height:
- windowBarStyle === Platform.WINDOWS || Platform.MACOS
- ? 'calc(100vh - 205px)'
- : 'calc(100vh - 175px)',
- position: 'absolute',
- right: 0,
- top: '75px',
- transition: {
- duration: 0.4,
- ease: 'anticipate',
- },
- width: '450px',
- x: '50vw',
- }),
- open: (windowBarStyle) => ({
- boxShadow: '0px 0px 10px 0px rgba(0, 0, 0, 0.8)',
- height:
- windowBarStyle === Platform.WINDOWS || Platform.MACOS
- ? 'calc(100vh - 205px)'
- : 'calc(100vh - 175px)',
- position: 'absolute',
- right: '20px',
- top: '75px',
- transition: {
- damping: 10,
- delay: 0,
- duration: 0.4,
- ease: 'anticipate',
- mass: 0.5,
- },
- width: '450px',
- x: 0,
- zIndex: 120,
- }),
-};
-
-const queueDrawerButtonVariants: Variants = {
- hidden: {
- opacity: 0,
- transition: { duration: 0.2 },
- x: 100,
- },
- visible: {
- opacity: 0.5,
- transition: { duration: 0.1, ease: 'anticipate' },
- x: 0,
- },
-};
-
-const queueSidebarVariants: Variants = {
- closed: (rightWidth) => ({
- transition: { duration: 0.5 },
- width: rightWidth,
- x: 1000,
- zIndex: 120,
- }),
- open: (rightWidth) => ({
- transition: {
- duration: 0.5,
- ease: 'anticipate',
- },
- width: rightWidth,
- x: 0,
- zIndex: 120,
- }),
-};
-
export const MainContent = ({ shell }: { shell?: boolean }) => {
- const sidebar = useAppStore((state) => state.sidebar);
- const { setSideBar } = useAppStoreActions();
- const [drawer, drawerHandler] = useDisclosure(false);
const location = useLocation();
- const { sideQueueType, showQueueDrawerButton } = useGeneralSettings();
- const { windowBarStyle } = useWindowSettings();
-
- const sidebarRef = useRef(null);
- const rightSidebarRef = useRef(null);
+ const { collapsed, leftWidth, rightWidth, rightExpanded } = useSidebarStore();
+ const { setSideBar } = useAppStoreActions();
+ const { sideQueueType } = useGeneralSettings();
const [isResizing, setIsResizing] = useState(false);
const [isResizingRight, setIsResizingRight] = useState(false);
+ const { showQueueDrawerButton } = useGeneralSettings();
- const drawerTimeout = useTimeout(() => drawerHandler.open(), 500);
-
- const handleEnterDrawerButton = useCallback(() => {
- drawerTimeout.start();
- }, [drawerTimeout]);
-
- const handleLeaveDrawerButton = useCallback(() => {
- drawerTimeout.clear();
- }, [drawerTimeout]);
-
- const isQueueDrawerButtonVisible =
- showQueueDrawerButton &&
- !sidebar.rightExpanded &&
- !drawer &&
- location.pathname !== AppRoute.NOW_PLAYING;
-
- const showSideQueue = sidebar.rightExpanded && location.pathname !== AppRoute.NOW_PLAYING;
- const { expanded: isFullScreenPlayerExpanded } = useFullScreenPlayerStore();
+ const showSideQueue = rightExpanded && location.pathname !== AppRoute.NOW_PLAYING;
+ const rightSidebarRef = useRef(null);
const startResizing = useCallback((position: 'left' | 'right') => {
if (position === 'left') return setIsResizing(true);
@@ -225,13 +72,13 @@ export const MainContent = ({ shell }: { shell?: boolean }) => {
setSideBar({ collapsed: false, leftWidth: constrainedWidth });
}
} else if (isResizingRight) {
- const start = Number(sidebar.rightWidth.split('px')[0]);
+ const start = Number(rightWidth.split('px')[0]);
const { left } = rightSidebarRef!.current!.getBoundingClientRect();
const width = `${constrainRightSidebarWidth(start + left - mouseMoveEvent.clientX)}px`;
setSideBar({ rightWidth: width });
}
},
- [isResizing, isResizingRight, setSideBar, sidebar.rightWidth],
+ [isResizing, isResizingRight, setSideBar, rightWidth],
);
const throttledResize = useMemo(() => throttle(resize, 50), [resize]);
@@ -248,123 +95,26 @@ export const MainContent = ({ shell }: { shell?: boolean }) => {
return (
{!shell && (
- <>
-
- {isFullScreenPlayerExpanded && }
-
-
-
- {isQueueDrawerButtonVisible && (
-
-
-
- )}
-
- {drawer && (
- {
- // The drawer will close due to the delay when setting isReorderingQueue
- setTimeout(() => {
- if (useAppStore.getState().isReorderingQueue) return;
- drawerHandler.close();
- }, 50);
- }}
- >
-
-
- )}
-
-
- {showSideQueue && (
- <>
- {sideQueueType === 'sideQueue' ? (
-
- ) : (
- {
- // The drawer will close due to the delay when setting isReorderingQueue
- setTimeout(() => {
- if (useAppStore.getState().isReorderingQueue) return;
- drawerHandler.close();
- }, 50);
- }}
- >
-
-
- )}
- >
- )}
-
- >
+ >}>
+ {showQueueDrawerButton && }
+
+
+
+
)}
>}>
diff --git a/src/renderer/layouts/default-layout/right-sidebar.tsx b/src/renderer/layouts/default-layout/right-sidebar.tsx
new file mode 100644
index 00000000..49ceb827
--- /dev/null
+++ b/src/renderer/layouts/default-layout/right-sidebar.tsx
@@ -0,0 +1,142 @@
+import { AnimatePresence, motion, Variants } from 'framer-motion';
+import { forwardRef, Ref } from 'react';
+import { useLocation } from 'react-router';
+import styled from 'styled-components';
+import { SidebarPlayQueue, DrawerPlayQueue } from '/@/renderer/features/now-playing';
+import { ResizeHandle } from '/@/renderer/features/shared';
+import { AppRoute } from '/@/renderer/router/routes';
+import { useGeneralSettings, useSidebarStore, useWindowSettings } from '/@/renderer/store';
+import { Platform } from '/@/renderer/types';
+
+const RightSidebarContainer = styled(motion.aside)`
+ position: relative;
+ grid-area: right-sidebar;
+ height: 100%;
+ background: var(--sidebar-bg);
+ border-left: var(--sidebar-border);
+`;
+
+const queueSidebarVariants: Variants = {
+ closed: (rightWidth) => ({
+ transition: { duration: 0.5 },
+ width: rightWidth,
+ x: 1000,
+ zIndex: 120,
+ }),
+ open: (rightWidth) => ({
+ transition: {
+ duration: 0.5,
+ ease: 'anticipate',
+ },
+ width: rightWidth,
+ x: 0,
+ zIndex: 120,
+ }),
+};
+
+const QueueDrawer = styled(motion.div)`
+ background: var(--main-bg);
+ border: 3px solid var(--generic-border-color);
+ border-radius: 10px;
+`;
+
+const queueDrawerVariants: Variants = {
+ closed: (windowBarStyle) => ({
+ height:
+ windowBarStyle === Platform.WINDOWS || Platform.MACOS
+ ? 'calc(100vh - 205px)'
+ : 'calc(100vh - 175px)',
+ position: 'absolute',
+ right: 0,
+ top: '75px',
+ transition: {
+ duration: 0.4,
+ ease: 'anticipate',
+ },
+ width: '450px',
+ x: '50vw',
+ }),
+ open: (windowBarStyle) => ({
+ boxShadow: '0px 0px 10px 0px rgba(0, 0, 0, 0.8)',
+ height:
+ windowBarStyle === Platform.WINDOWS || Platform.MACOS
+ ? 'calc(100vh - 205px)'
+ : 'calc(100vh - 175px)',
+ position: 'absolute',
+ right: '20px',
+ top: '75px',
+ transition: {
+ damping: 10,
+ delay: 0,
+ duration: 0.4,
+ ease: 'anticipate',
+ mass: 0.5,
+ },
+ width: '450px',
+ x: 0,
+ zIndex: 120,
+ }),
+};
+
+interface RightSidebarProps {
+ isResizing: boolean;
+ startResizing: (direction: 'left' | 'right') => void;
+}
+
+export const RightSidebar = forwardRef(
+ ({ isResizing: isResizingRight, startResizing }: RightSidebarProps, ref: Ref) => {
+ const { windowBarStyle } = useWindowSettings();
+ const { rightWidth, rightExpanded } = useSidebarStore();
+ const { sideQueueType } = useGeneralSettings();
+ const location = useLocation();
+ const showSideQueue = rightExpanded && location.pathname !== AppRoute.NOW_PLAYING;
+
+ return (
+
+ {showSideQueue && (
+ <>
+ {sideQueueType === 'sideQueue' ? (
+
+ ) : (
+
+
+
+ )}
+ >
+ )}
+
+ );
+ },
+);
diff --git a/src/renderer/layouts/default-layout/side-drawer-queue.tsx b/src/renderer/layouts/default-layout/side-drawer-queue.tsx
new file mode 100644
index 00000000..84230218
--- /dev/null
+++ b/src/renderer/layouts/default-layout/side-drawer-queue.tsx
@@ -0,0 +1,141 @@
+import { useDisclosure, useTimeout } from '@mantine/hooks';
+import { AnimatePresence, motion, Variants } from 'framer-motion';
+import { useCallback } from 'react';
+import { TbArrowBarLeft } from 'react-icons/tb';
+import { useLocation } from 'react-router';
+import styled from 'styled-components';
+import { DrawerPlayQueue } from '/@/renderer/features/now-playing';
+import { AppRoute } from '/@/renderer/router/routes';
+import { useAppStore, useSidebarStore } from '/@/renderer/store';
+import { Platform } from '/@/renderer/types';
+
+const QueueDrawerArea = styled(motion.div)`
+ position: absolute;
+ top: 50%;
+ right: 25px;
+ z-index: 100;
+ display: flex;
+ align-items: center;
+ width: 20px;
+ height: 30px;
+ user-select: none;
+`;
+
+const QueueDrawer = styled(motion.div)`
+ background: var(--main-bg);
+ border: 3px solid var(--generic-border-color);
+ border-radius: 10px;
+`;
+
+const queueDrawerVariants: Variants = {
+ closed: (windowBarStyle) => ({
+ height:
+ windowBarStyle === Platform.WINDOWS || Platform.MACOS
+ ? 'calc(100vh - 205px)'
+ : 'calc(100vh - 175px)',
+ position: 'absolute',
+ right: 0,
+ top: '75px',
+ transition: {
+ duration: 0.4,
+ ease: 'anticipate',
+ },
+ width: '450px',
+ x: '50vw',
+ }),
+ open: (windowBarStyle) => ({
+ boxShadow: '0px 0px 10px 0px rgba(0, 0, 0, 0.8)',
+ height:
+ windowBarStyle === Platform.WINDOWS || Platform.MACOS
+ ? 'calc(100vh - 205px)'
+ : 'calc(100vh - 175px)',
+ position: 'absolute',
+ right: '20px',
+ top: '75px',
+ transition: {
+ damping: 10,
+ delay: 0,
+ duration: 0.4,
+ ease: 'anticipate',
+ mass: 0.5,
+ },
+ width: '450px',
+ x: 0,
+ zIndex: 120,
+ }),
+};
+
+const queueDrawerButtonVariants: Variants = {
+ hidden: {
+ opacity: 0,
+ transition: { duration: 0.2 },
+ x: 100,
+ },
+ visible: {
+ opacity: 0.5,
+ transition: { duration: 0.1, ease: 'anticipate' },
+ x: 0,
+ },
+};
+
+export const SideDrawerQueue = () => {
+ const location = useLocation();
+ const [drawer, drawerHandler] = useDisclosure(false);
+ const { rightExpanded } = useSidebarStore();
+
+ const drawerTimeout = useTimeout(() => drawerHandler.open(), 500);
+
+ const handleEnterDrawerButton = useCallback(() => {
+ drawerTimeout.start();
+ }, [drawerTimeout]);
+
+ const handleLeaveDrawerButton = useCallback(() => {
+ drawerTimeout.clear();
+ }, [drawerTimeout]);
+
+ const isQueueDrawerButtonVisible =
+ !rightExpanded && !drawer && location.pathname !== AppRoute.NOW_PLAYING;
+
+ return (
+ <>
+
+ {isQueueDrawerButtonVisible && (
+
+
+
+ )}
+
+ {drawer && (
+ {
+ // The drawer will close due to the delay when setting isReorderingQueue
+ setTimeout(() => {
+ if (useAppStore.getState().isReorderingQueue) return;
+ drawerHandler.close();
+ }, 50);
+ }}
+ >
+
+
+ )}
+
+ >
+ );
+};