Mobile UX fixes
This commit is contained in:
@@ -0,0 +1,158 @@
|
||||
/**
|
||||
* @ai-summary Hamburger menu drawer for mobile navigation
|
||||
* @ai-context Bottom slide-up drawer with all navigation options
|
||||
*/
|
||||
|
||||
import React from 'react';
|
||||
import {
|
||||
Box,
|
||||
SwipeableDrawer,
|
||||
List,
|
||||
ListItemButton,
|
||||
ListItemIcon,
|
||||
ListItemText,
|
||||
Typography,
|
||||
useTheme
|
||||
} from '@mui/material';
|
||||
import HomeRoundedIcon from '@mui/icons-material/HomeRounded';
|
||||
import DirectionsCarRoundedIcon from '@mui/icons-material/DirectionsCarRounded';
|
||||
import LocalGasStationRoundedIcon from '@mui/icons-material/LocalGasStationRounded';
|
||||
import DescriptionRoundedIcon from '@mui/icons-material/DescriptionRounded';
|
||||
import SettingsRoundedIcon from '@mui/icons-material/SettingsRounded';
|
||||
import { MobileScreen } from '../../../core/store/navigation';
|
||||
|
||||
// iOS swipeable drawer configuration
|
||||
const iOS = typeof navigator !== 'undefined' && /iPad|iPhone|iPod/.test(navigator.userAgent);
|
||||
|
||||
interface HamburgerDrawerProps {
|
||||
open: boolean;
|
||||
onClose: () => void;
|
||||
onNavigate: (screen: MobileScreen) => void;
|
||||
activeScreen: MobileScreen;
|
||||
}
|
||||
|
||||
interface MenuItem {
|
||||
screen: MobileScreen;
|
||||
label: string;
|
||||
icon: React.ReactNode;
|
||||
}
|
||||
|
||||
// Menu items from bottom to top (reversed order in array for rendering)
|
||||
const menuItems: MenuItem[] = [
|
||||
{ screen: 'Settings', label: 'Settings', icon: <SettingsRoundedIcon /> },
|
||||
{ screen: 'Documents', label: 'Documents', icon: <DescriptionRoundedIcon /> },
|
||||
{ screen: 'Stations', label: 'Stations', icon: <LocalGasStationRoundedIcon /> },
|
||||
{ screen: 'Log Fuel', label: 'Log Fuel', icon: <LocalGasStationRoundedIcon /> },
|
||||
{ screen: 'Vehicles', label: 'Vehicles', icon: <DirectionsCarRoundedIcon /> },
|
||||
{ screen: 'Dashboard', label: 'Dashboard', icon: <HomeRoundedIcon /> },
|
||||
];
|
||||
|
||||
export const HamburgerDrawer: React.FC<HamburgerDrawerProps> = ({
|
||||
open,
|
||||
onClose,
|
||||
onNavigate,
|
||||
activeScreen
|
||||
}) => {
|
||||
const theme = useTheme();
|
||||
|
||||
const handleNavigate = (screen: MobileScreen) => {
|
||||
onNavigate(screen);
|
||||
onClose();
|
||||
};
|
||||
|
||||
return (
|
||||
<SwipeableDrawer
|
||||
anchor="bottom"
|
||||
open={open}
|
||||
onClose={onClose}
|
||||
onOpen={() => {}}
|
||||
disableSwipeToOpen
|
||||
disableBackdropTransition={!iOS}
|
||||
disableDiscovery={iOS}
|
||||
sx={{
|
||||
'& .MuiDrawer-paper': {
|
||||
borderTopLeftRadius: 16,
|
||||
borderTopRightRadius: 16,
|
||||
maxHeight: '60vh',
|
||||
overflow: 'visible'
|
||||
}
|
||||
}}
|
||||
>
|
||||
<Box sx={{ pb: 4 }}>
|
||||
{/* Drawer handle */}
|
||||
<Box
|
||||
sx={{
|
||||
width: 40,
|
||||
height: 4,
|
||||
backgroundColor: theme.palette.divider,
|
||||
borderRadius: 2,
|
||||
margin: '12px auto 8px'
|
||||
}}
|
||||
/>
|
||||
|
||||
{/* Header */}
|
||||
<Typography
|
||||
variant="subtitle1"
|
||||
sx={{
|
||||
fontWeight: 600,
|
||||
px: 2,
|
||||
py: 1.5,
|
||||
color: theme.palette.text.secondary
|
||||
}}
|
||||
>
|
||||
Menu
|
||||
</Typography>
|
||||
|
||||
{/* Menu Items */}
|
||||
<List disablePadding>
|
||||
{menuItems.map(({ screen, label, icon }) => {
|
||||
const isActive = activeScreen === screen;
|
||||
return (
|
||||
<ListItemButton
|
||||
key={screen}
|
||||
onClick={() => handleNavigate(screen)}
|
||||
sx={{
|
||||
py: 1.5,
|
||||
px: 2,
|
||||
minHeight: 56,
|
||||
backgroundColor: isActive
|
||||
? theme.palette.primary.main + '14'
|
||||
: 'transparent',
|
||||
borderLeft: isActive
|
||||
? `3px solid ${theme.palette.primary.main}`
|
||||
: '3px solid transparent',
|
||||
'&:hover': {
|
||||
backgroundColor: isActive
|
||||
? theme.palette.primary.main + '20'
|
||||
: theme.palette.action.hover
|
||||
}
|
||||
}}
|
||||
>
|
||||
<ListItemIcon
|
||||
sx={{
|
||||
minWidth: 40,
|
||||
color: isActive
|
||||
? theme.palette.primary.main
|
||||
: theme.palette.text.secondary
|
||||
}}
|
||||
>
|
||||
{icon}
|
||||
</ListItemIcon>
|
||||
<ListItemText
|
||||
primary={label}
|
||||
primaryTypographyProps={{
|
||||
fontWeight: isActive ? 600 : 500,
|
||||
fontSize: '0.95rem',
|
||||
color: isActive
|
||||
? theme.palette.primary.main
|
||||
: theme.palette.text.primary
|
||||
}}
|
||||
/>
|
||||
</ListItemButton>
|
||||
);
|
||||
})}
|
||||
</List>
|
||||
</Box>
|
||||
</SwipeableDrawer>
|
||||
);
|
||||
};
|
||||
Reference in New Issue
Block a user