Files
holy-flavor-rater/frontend/src/components/layout/CategoryBar.tsx
T
2026-09-04 23:38:55 +02:00

116 lines
3.7 KiB
TypeScript

import { Link, useLocation } from 'react-router-dom';
import { Box, Button, alpha } from '@mui/material';
import type { Theme } from '@mui/material';
import HomeOutlinedIcon from '@mui/icons-material/HomeOutlined';
import GroupsOutlinedIcon from '@mui/icons-material/GroupsOutlined';
import CollectionsBookmarkOutlinedIcon from '@mui/icons-material/CollectionsBookmarkOutlined';
import { useTranslation } from 'react-i18next';
import { useAuth } from '../../hooks/useAuth';
export interface CategoryBarProps {
categories: { name: string; slug: string }[];
}
/**
* The browse row. Categories are the dominant browse axis, so they get a
* scrollable row of their own with Home / Community / Dashboard leading it.
*
* This renders as the *second row inside* the nav pane (see `GlassAppBar`) —
* it deliberately carries no sticky positioning or glass of its own, so the
* two rows share one pane, one rim and one blur pass instead of stacking two
* translucent bars on top of each other.
*/
export const CategoryBar = ({ categories }: CategoryBarProps) => {
const { t } = useTranslation();
const location = useLocation();
const { user } = useAuth();
const isActive = (path: string) =>
path === '/' ? location.pathname === '/' : location.pathname.startsWith(path);
const tabSx = (active: boolean) => ({
flexShrink: 0,
borderRadius: (theme: Theme) => `${theme.tokens.radius.pill}px`,
px: 1.75,
py: 0.75,
fontWeight: active ? 700 : 500,
color: active ? 'primary.main' : 'text.secondary',
bgcolor: active ? (theme: Theme) => alpha(theme.palette.primary.main, 0.14) : 'transparent',
border: '1px solid',
borderColor: active ? (theme: Theme) => alpha(theme.palette.primary.main, 0.35) : 'transparent',
transition: (theme: Theme) =>
`background-color ${theme.tokens.motion.fast} ${theme.tokens.motion.ease}, border-color ${theme.tokens.motion.fast} ${theme.tokens.motion.ease}`,
'&:hover': {
color: 'text.primary',
bgcolor: 'action.hover',
},
});
return (
<Box
component="nav"
sx={{
maxWidth: 1400,
mx: 'auto',
px: { xs: 1, sm: 4, md: 6 },
display: 'flex',
alignItems: 'center',
gap: { xs: 0.5, md: 1 },
minHeight: { xs: 46, md: 52 },
overflowX: 'auto',
'&::-webkit-scrollbar': { display: 'none' },
scrollbarWidth: 'none',
}}
>
<Button
component={Link}
to="/"
disableRipple
sx={{ ...tabSx(isActive('/')), ml: { xs: -0.5, md: -1.75 } }}
>
<HomeOutlinedIcon sx={{ fontSize: '1.1rem', mr: 0.75 }} />
{t('nav.home')}
</Button>
{user && (
<Button component={Link} to="/community" disableRipple sx={tabSx(isActive('/community'))}>
<GroupsOutlinedIcon sx={{ fontSize: '1.1rem', mr: 0.75 }} />
{t('nav.community')}
</Button>
)}
{user && (
<Button component={Link} to="/dashboard" disableRipple sx={tabSx(isActive('/dashboard'))}>
<CollectionsBookmarkOutlinedIcon sx={{ fontSize: '1.1rem', mr: 0.75 }} />
{t('nav.dashboard')}
</Button>
)}
<Box
sx={{
alignSelf: 'stretch',
my: 1.25,
mx: 0.5,
borderLeft: '1px solid',
borderColor: 'divider',
}}
/>
{categories.map((cat) => {
const path = `/category/${cat.slug}`;
return (
<Button
key={cat.slug}
component={Link}
to={path}
disableRipple
sx={tabSx(location.pathname === path)}
>
{t(`categories.${cat.slug}`, { defaultValue: cat.name })}
</Button>
);
})}
</Box>
);
};