add basic mobile responsive layout

This commit is contained in:
jeffvli
2025-11-19 19:23:44 -08:00
parent 485fe8085c
commit c763824803
26 changed files with 1930 additions and 40 deletions
@@ -0,0 +1,55 @@
.layout {
position: relative;
display: grid;
grid-template-areas:
'window-bar'
'main-content'
'player';
grid-template-rows: 0 calc(100vh - 90px) 90px;
grid-template-columns: 1fr;
gap: 0;
width: 100vw;
height: 100vh;
overflow: hidden;
background: var(--theme-colors-background);
}
.drawer-button {
position: absolute;
bottom: calc(90px + 0.75rem);
left: 0.75rem;
z-index: 100;
background: color-mix(in srgb, var(--theme-colors-background) 90%, transparent);
border: 1px solid var(--theme-colors-border);
backdrop-filter: blur(10px);
}
.main-content {
position: relative;
grid-area: main-content;
overflow-x: hidden;
overflow-y: auto;
-webkit-overflow-scrolling: touch;
}
.full-screen-player-overlay {
position: fixed;
top: 0;
right: 0;
bottom: 0;
left: 0;
z-index: 200;
visibility: hidden;
pointer-events: none;
background: var(--theme-colors-background);
opacity: 0;
transition:
opacity 0.3s ease-in-out,
visibility 0.3s ease-in-out;
}
.full-screen-player-visible {
visibility: visible;
pointer-events: auto;
opacity: 1;
}
@@ -0,0 +1,92 @@
import clsx from 'clsx';
import { lazy } from 'react';
import { Outlet, useNavigate } from 'react-router';
import styles from './mobile-layout.module.css';
import { ContextMenuController } from '/@/renderer/features/context-menu/context-menu-controller';
import { MobileFullscreenPlayer } from '/@/renderer/features/player/components/mobile-fullscreen-player';
import { CommandPalette } from '/@/renderer/features/search/components/command-palette';
import { MobileSidebar } from '/@/renderer/features/sidebar/components/mobile-sidebar';
import { PlayerBar } from '/@/renderer/layouts/default-layout/player-bar';
import { AppRoute } from '/@/renderer/router/routes';
import { useFullScreenPlayerStore } from '/@/renderer/store';
import { useCommandPalette } from '/@/renderer/store';
import { useHotkeySettings } from '/@/renderer/store/settings.store';
import { ActionIcon } from '/@/shared/components/action-icon/action-icon';
import { Drawer } from '/@/shared/components/drawer/drawer';
import { useDisclosure } from '/@/shared/hooks/use-disclosure';
import { useHotkeys } from '/@/shared/hooks/use-hotkeys';
const WindowBar = lazy(() =>
import('/@/renderer/layouts/window-bar').then((module) => ({
default: module.WindowBar,
})),
);
interface MobileLayoutProps {
shell?: boolean;
}
export const MobileLayout = ({ shell }: MobileLayoutProps) => {
const { opened, ...handlers } = useCommandPalette();
const { bindings } = useHotkeySettings();
const navigate = useNavigate();
const [sidebarOpened, { close: closeSidebar, open: openSidebar }] = useDisclosure(false);
const { expanded: isFullScreenPlayerExpanded } = useFullScreenPlayerStore();
useHotkeys([
[bindings.globalSearch.hotkey, () => handlers.open()],
[bindings.browserBack.hotkey, () => navigate(-1)],
[bindings.browserForward.hotkey, () => navigate(1)],
[bindings.navigateHome.hotkey, () => navigate(AppRoute.HOME)],
]);
return (
<>
<div className={clsx(styles.layout)} id="mobile-layout">
{!shell && <WindowBar />}
<ActionIcon
className={styles.drawerButton}
icon="menu"
onClick={openSidebar}
size="lg"
tooltip={{ label: 'Menu' }}
variant="subtle"
/>
<main className={styles.mainContent}>
<Outlet />
</main>
<PlayerBar />
</div>
<Drawer
onClose={closeSidebar}
opened={sidebarOpened}
position="left"
size="320px"
styles={{
body: {
height: '100%',
padding: 0,
},
content: {
height: '100%',
width: '100%',
},
}}
withCloseButton={false}
>
<MobileSidebar />
</Drawer>
<div
className={clsx(styles.fullScreenPlayerOverlay, {
[styles.fullScreenPlayerVisible]: isFullScreenPlayerExpanded,
})}
>
<MobileFullscreenPlayer />
</div>
<CommandPalette modalProps={{ handlers, opened }} />
<ContextMenuController.Root />
</>
);
};
@@ -0,0 +1,17 @@
import { useIsMobile } from '/@/renderer/hooks/use-is-mobile';
import { DefaultLayout } from '/@/renderer/layouts/default-layout';
import { MobileLayout } from '/@/renderer/layouts/mobile-layout/mobile-layout';
interface ResponsiveLayoutProps {
shell?: boolean;
}
export const ResponsiveLayout = ({ shell }: ResponsiveLayoutProps) => {
const isMobile = useIsMobile();
if (isMobile) {
return <MobileLayout shell={shell} />;
}
return <DefaultLayout shell={shell} />;
};