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,46 @@
.container {
display: flex;
flex-direction: column;
gap: var(--mantine-spacing-xs);
width: 100%;
height: 100%;
background: var(--theme-colors-background-alternate);
}
.scroll-area {
flex: 1;
min-height: 0;
padding: 0 var(--theme-spacing-md) var(--theme-spacing-md) var(--theme-spacing-md);
}
.accordion-root {
height: 100%;
}
.accordion-item {
border-bottom: none;
}
.accordion-control {
height: 2.5rem;
border-radius: var(--theme-radius-md);
&:hover {
background: var(--theme-colors-background);
}
}
.accordion-content {
padding: 0;
background: var(--theme-colors-background-alternate);
}
.accordion-content:last-child {
padding-bottom: var(--theme-spacing-md);
}
.server-selector-wrapper {
position: relative;
z-index: 1;
flex-shrink: 0;
}
@@ -0,0 +1,112 @@
import { useMemo } from 'react';
import { useTranslation } from 'react-i18next';
import { useLocation } from 'react-router';
import styles from './mobile-sidebar.module.css';
import { ActionBar } from '/@/renderer/features/sidebar/components/action-bar';
import { ServerSelector } from '/@/renderer/features/sidebar/components/server-selector';
import { SidebarIcon } from '/@/renderer/features/sidebar/components/sidebar-icon';
import { SidebarItem } from '/@/renderer/features/sidebar/components/sidebar-item';
import {
SidebarPlaylistList,
SidebarSharedPlaylistList,
} from '/@/renderer/features/sidebar/components/sidebar-playlist-list';
import { SidebarItemType, useGeneralSettings } from '/@/renderer/store/settings.store';
import { Accordion } from '/@/shared/components/accordion/accordion';
import { Group } from '/@/shared/components/group/group';
import { ScrollArea } from '/@/shared/components/scroll-area/scroll-area';
import { Text } from '/@/shared/components/text/text';
export const MobileSidebar = () => {
const { t } = useTranslation();
const location = useLocation();
const { sidebarPlaylistList } = useGeneralSettings();
const translatedSidebarItemMap = useMemo(
() => ({
Albums: t('page.sidebar.albums', { postProcess: 'titleCase' }),
Artists: t('page.sidebar.albumArtists', { postProcess: 'titleCase' }),
'Artists-all': t('page.sidebar.artists', { postProcess: 'titleCase' }),
Genres: t('page.sidebar.genres', { postProcess: 'titleCase' }),
Home: t('page.sidebar.home', { postProcess: 'titleCase' }),
'Now Playing': t('page.sidebar.nowPlaying', { postProcess: 'titleCase' }),
Playlists: t('page.sidebar.playlists', { postProcess: 'titleCase' }),
Search: t('page.sidebar.search', { postProcess: 'titleCase' }),
Settings: t('page.sidebar.settings', { postProcess: 'titleCase' }),
Tracks: t('page.sidebar.tracks', { postProcess: 'titleCase' }),
}),
[t],
);
const { sidebarItems } = useGeneralSettings();
const sidebarItemsWithRoute: SidebarItemType[] = useMemo(() => {
if (!sidebarItems) return [];
const items = sidebarItems
.filter((item) => !item.disabled)
.map((item) => ({
...item,
label:
translatedSidebarItemMap[item.id as keyof typeof translatedSidebarItemMap] ??
item.label,
}));
return items;
}, [sidebarItems, translatedSidebarItemMap]);
return (
<div className={styles.container} id="mobile-sidebar">
<Group grow id="global-search-container" style={{ flexShrink: 0 }}>
<ActionBar />
</Group>
<ScrollArea allowDragScroll className={styles.scrollArea}>
<Accordion
classNames={{
content: styles.accordionContent,
control: styles.accordionControl,
item: styles.accordionItem,
root: styles.accordionRoot,
}}
defaultValue={['library', 'playlists']}
multiple
>
<Accordion.Item value="library">
<Accordion.Control>
<Text fw={600} variant="secondary">
{t('page.sidebar.myLibrary', {
postProcess: 'titleCase',
})}
</Text>
</Accordion.Control>
<Accordion.Panel>
{sidebarItemsWithRoute.map((item) => {
return (
<SidebarItem key={`sidebar-${item.route}`} to={item.route}>
<Group gap="sm">
<SidebarIcon
active={location.pathname === item.route}
route={item.route}
/>
{item.label}
</Group>
</SidebarItem>
);
})}
</Accordion.Panel>
</Accordion.Item>
{sidebarPlaylistList && (
<>
<SidebarPlaylistList />
<SidebarSharedPlaylistList />
</>
)}
</Accordion>
</ScrollArea>
<div className={styles.serverSelectorWrapper}>
<ServerSelector showImage={false} />
</div>
</div>
);
};