mirror of
https://github.com/Unleash/unleash.git
synced 2025-06-14 01:16:17 +02:00
feat: user profile preview (#7150)
This commit is contained in:
parent
e5aa1a81cb
commit
02c0744fc2
@ -216,7 +216,7 @@ const ShowHide: FC<{ mode: 'full' | 'mini'; onChange: () => void }> = ({
|
|||||||
display: 'flex',
|
display: 'flex',
|
||||||
justifyContent: 'space-between',
|
justifyContent: 'space-between',
|
||||||
alignItems: 'center',
|
alignItems: 'center',
|
||||||
margin: theme.spacing(2, 1, 0, 2),
|
margin: theme.spacing(2, 1, 0, mode === 'mini' ? 1 : 2),
|
||||||
})}
|
})}
|
||||||
>
|
>
|
||||||
{mode === 'full' && (
|
{mode === 'full' && (
|
||||||
|
@ -2,7 +2,14 @@ import { useState, type VFC } from 'react';
|
|||||||
import useMediaQuery from '@mui/material/useMediaQuery';
|
import useMediaQuery from '@mui/material/useMediaQuery';
|
||||||
import { useTheme } from '@mui/material/styles';
|
import { useTheme } from '@mui/material/styles';
|
||||||
import { Link } from 'react-router-dom';
|
import { Link } from 'react-router-dom';
|
||||||
import { AppBar, Box, IconButton, styled, Tooltip } from '@mui/material';
|
import {
|
||||||
|
AppBar,
|
||||||
|
Box,
|
||||||
|
Divider,
|
||||||
|
IconButton,
|
||||||
|
styled,
|
||||||
|
Tooltip,
|
||||||
|
} from '@mui/material';
|
||||||
import MenuIcon from '@mui/icons-material/Menu';
|
import MenuIcon from '@mui/icons-material/Menu';
|
||||||
import UserProfile from 'component/user/UserProfile';
|
import UserProfile from 'component/user/UserProfile';
|
||||||
import { ConditionallyRender } from 'component/common/ConditionallyRender/ConditionallyRender';
|
import { ConditionallyRender } from 'component/common/ConditionallyRender/ConditionallyRender';
|
||||||
@ -226,6 +233,12 @@ const Header: VFC = () => {
|
|||||||
<MenuBookIcon />
|
<MenuBookIcon />
|
||||||
</StyledIconButton>
|
</StyledIconButton>
|
||||||
</Tooltip>
|
</Tooltip>
|
||||||
|
<Divider
|
||||||
|
orientation='vertical'
|
||||||
|
variant='middle'
|
||||||
|
flexItem
|
||||||
|
sx={{ ml: 1 }}
|
||||||
|
/>
|
||||||
<UserProfile />
|
<UserProfile />
|
||||||
</StyledUserContainer>
|
</StyledUserContainer>
|
||||||
</StyledNav>
|
</StyledNav>
|
||||||
|
@ -1,38 +1,43 @@
|
|||||||
import { useState } from 'react';
|
import { useState } from 'react';
|
||||||
import { ClickAwayListener, IconButton, Tooltip, styled } from '@mui/material';
|
import {
|
||||||
|
Box,
|
||||||
|
Button,
|
||||||
|
ClickAwayListener,
|
||||||
|
styled,
|
||||||
|
Typography,
|
||||||
|
} from '@mui/material';
|
||||||
import { UserProfileContent } from './UserProfileContent/UserProfileContent';
|
import { UserProfileContent } from './UserProfileContent/UserProfileContent';
|
||||||
import type { IUser } from 'interfaces/user';
|
import type { IUser } from 'interfaces/user';
|
||||||
import { HEADER_USER_AVATAR } from 'utils/testIds';
|
|
||||||
import { useId } from 'hooks/useId';
|
import { useId } from 'hooks/useId';
|
||||||
import { UserAvatar } from 'component/common/UserAvatar/UserAvatar';
|
import { UserAvatar } from 'component/common/UserAvatar/UserAvatar';
|
||||||
import { flexRow, itemsCenter } from 'themes/themeStyles';
|
import ExpandMoreIcon from '@mui/icons-material/ExpandMore';
|
||||||
|
import ExpandLessIcon from '@mui/icons-material/ExpandLess';
|
||||||
const StyledUserAvatar = styled(UserAvatar)(({ theme }) => ({
|
import { HEADER_USER_AVATAR } from 'utils/testIds';
|
||||||
width: theme.spacing(4.5),
|
|
||||||
height: theme.spacing(4.5),
|
|
||||||
}));
|
|
||||||
|
|
||||||
const StyledProfileContainer = styled('div')(({ theme }) => ({
|
const StyledProfileContainer = styled('div')(({ theme }) => ({
|
||||||
position: 'relative',
|
position: 'relative',
|
||||||
}));
|
marginLeft: theme.spacing(2),
|
||||||
|
cursor: 'pointer',
|
||||||
const StyledIconButton = styled(IconButton)(({ theme }) => ({
|
|
||||||
...flexRow,
|
|
||||||
...itemsCenter,
|
|
||||||
color: 'inherit',
|
|
||||||
padding: theme.spacing(1),
|
|
||||||
'&:focus-visible': {
|
|
||||||
outlineStyle: 'solid',
|
|
||||||
outlineWidth: 2,
|
|
||||||
outlineColor: theme.palette.primary.main,
|
|
||||||
borderRadius: '100%',
|
|
||||||
},
|
|
||||||
}));
|
}));
|
||||||
|
|
||||||
interface IUserProfileProps {
|
interface IUserProfileProps {
|
||||||
profile: IUser;
|
profile: IUser;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
const StyledUserAvatar = styled(UserAvatar)(({ theme }) => ({
|
||||||
|
width: theme.spacing(4.75),
|
||||||
|
height: theme.spacing(4.75),
|
||||||
|
marginRight: theme.spacing(1.5),
|
||||||
|
}));
|
||||||
|
|
||||||
|
const StyledSubtitle = styled(Typography)(({ theme }) => ({
|
||||||
|
color: theme.palette.text.secondary,
|
||||||
|
overflow: 'hidden',
|
||||||
|
textOverflow: 'ellipsis',
|
||||||
|
whiteSpace: 'nowrap',
|
||||||
|
maxWidth: theme.spacing(35),
|
||||||
|
}));
|
||||||
|
|
||||||
const UserProfile = ({ profile }: IUserProfileProps) => {
|
const UserProfile = ({ profile }: IUserProfileProps) => {
|
||||||
const [showProfile, setShowProfile] = useState(false);
|
const [showProfile, setShowProfile] = useState(false);
|
||||||
const modalId = useId();
|
const modalId = useId();
|
||||||
@ -40,20 +45,28 @@ const UserProfile = ({ profile }: IUserProfileProps) => {
|
|||||||
return (
|
return (
|
||||||
<ClickAwayListener onClickAway={() => setShowProfile(false)}>
|
<ClickAwayListener onClickAway={() => setShowProfile(false)}>
|
||||||
<StyledProfileContainer>
|
<StyledProfileContainer>
|
||||||
<Tooltip title='Profile' arrow>
|
<Button
|
||||||
<StyledIconButton
|
component={Box}
|
||||||
onClick={() => setShowProfile((prev) => !prev)}
|
sx={{ display: 'flex', alignItems: 'center' }}
|
||||||
aria-controls={showProfile ? modalId : undefined}
|
aria-controls={showProfile ? modalId : undefined}
|
||||||
aria-expanded={showProfile}
|
aria-expanded={showProfile}
|
||||||
color='secondary'
|
onClick={() => setShowProfile((prev) => !prev)}
|
||||||
size='large'
|
>
|
||||||
>
|
<StyledUserAvatar
|
||||||
<StyledUserAvatar
|
user={profile}
|
||||||
user={profile}
|
data-testid={HEADER_USER_AVATAR}
|
||||||
data-testid={HEADER_USER_AVATAR}
|
/>
|
||||||
/>
|
<Box sx={{ mr: 3 }}>
|
||||||
</StyledIconButton>
|
<Typography>
|
||||||
</Tooltip>
|
{profile.name || profile.username}
|
||||||
|
</Typography>
|
||||||
|
<StyledSubtitle variant='body2' title={profile.email}>
|
||||||
|
{profile.email}
|
||||||
|
</StyledSubtitle>
|
||||||
|
</Box>
|
||||||
|
{showProfile ? <ExpandLessIcon /> : <ExpandMoreIcon />}
|
||||||
|
</Button>
|
||||||
|
|
||||||
<UserProfileContent
|
<UserProfileContent
|
||||||
id={modalId}
|
id={modalId}
|
||||||
showProfile={showProfile}
|
showProfile={showProfile}
|
||||||
|
@ -1,10 +1,9 @@
|
|||||||
import { ConditionallyRender } from 'component/common/ConditionallyRender/ConditionallyRender';
|
import { ConditionallyRender } from 'component/common/ConditionallyRender/ConditionallyRender';
|
||||||
import { Button, Paper, Typography, styled, Link } from '@mui/material';
|
import { Button, Link, Paper, styled } from '@mui/material';
|
||||||
import { basePath } from 'utils/formatPath';
|
import { basePath } from 'utils/formatPath';
|
||||||
import type { IUser } from 'interfaces/user';
|
import type { IUser } from 'interfaces/user';
|
||||||
import OpenInNew from '@mui/icons-material/OpenInNew';
|
import OpenInNew from '@mui/icons-material/OpenInNew';
|
||||||
import { Link as RouterLink } from 'react-router-dom';
|
import { Link as RouterLink } from 'react-router-dom';
|
||||||
import { UserAvatar } from 'component/common/UserAvatar/UserAvatar';
|
|
||||||
|
|
||||||
const StyledPaper = styled(Paper)(({ theme }) => ({
|
const StyledPaper = styled(Paper)(({ theme }) => ({
|
||||||
display: 'flex',
|
display: 'flex',
|
||||||
@ -15,35 +14,15 @@ const StyledPaper = styled(Paper)(({ theme }) => ({
|
|||||||
boxShadow: theme.boxShadows.popup,
|
boxShadow: theme.boxShadows.popup,
|
||||||
position: 'absolute',
|
position: 'absolute',
|
||||||
zIndex: 5000,
|
zIndex: 5000,
|
||||||
minWidth: theme.spacing(37.5),
|
minWidth: theme.spacing(34),
|
||||||
right: 0,
|
right: 0,
|
||||||
|
marginTop: theme.spacing(0.25),
|
||||||
[theme.breakpoints.down('md')]: {
|
[theme.breakpoints.down('md')]: {
|
||||||
width: '100%',
|
width: '100%',
|
||||||
padding: '1rem',
|
padding: '1rem',
|
||||||
},
|
},
|
||||||
}));
|
}));
|
||||||
|
|
||||||
const StyledProfileInfo = styled('div')(({ theme }) => ({
|
|
||||||
alignSelf: 'flex-start',
|
|
||||||
display: 'flex',
|
|
||||||
alignItems: 'center',
|
|
||||||
marginBottom: theme.spacing(2),
|
|
||||||
}));
|
|
||||||
|
|
||||||
const StyledUserAvatar = styled(UserAvatar)(({ theme }) => ({
|
|
||||||
width: theme.spacing(4.75),
|
|
||||||
height: theme.spacing(4.75),
|
|
||||||
marginRight: theme.spacing(1.5),
|
|
||||||
}));
|
|
||||||
|
|
||||||
const StyledSubtitle = styled(Typography)(({ theme }) => ({
|
|
||||||
color: theme.palette.text.secondary,
|
|
||||||
overflow: 'hidden',
|
|
||||||
textOverflow: 'ellipsis',
|
|
||||||
whiteSpace: 'nowrap',
|
|
||||||
maxWidth: theme.spacing(35),
|
|
||||||
}));
|
|
||||||
|
|
||||||
const StyledLink = styled(Link<typeof RouterLink | 'a'>)(({ theme }) => ({
|
const StyledLink = styled(Link<typeof RouterLink | 'a'>)(({ theme }) => ({
|
||||||
display: 'flex',
|
display: 'flex',
|
||||||
alignItems: 'center',
|
alignItems: 'center',
|
||||||
@ -88,18 +67,6 @@ export const UserProfileContent = ({
|
|||||||
condition={showProfile}
|
condition={showProfile}
|
||||||
show={
|
show={
|
||||||
<StyledPaper className='dropdown-outline' id={id}>
|
<StyledPaper className='dropdown-outline' id={id}>
|
||||||
<StyledProfileInfo>
|
|
||||||
<StyledUserAvatar user={profile} />
|
|
||||||
<div>
|
|
||||||
<Typography>
|
|
||||||
{profile.name || profile.username}
|
|
||||||
</Typography>
|
|
||||||
<StyledSubtitle variant='body2' title={profile.email}>
|
|
||||||
{profile.email}
|
|
||||||
</StyledSubtitle>
|
|
||||||
</div>
|
|
||||||
</StyledProfileInfo>
|
|
||||||
|
|
||||||
<StyledLink
|
<StyledLink
|
||||||
component={RouterLink}
|
component={RouterLink}
|
||||||
to='/profile'
|
to='/profile'
|
||||||
|
Loading…
Reference in New Issue
Block a user