mirror of
				https://github.com/Unleash/unleash.git
				synced 2025-10-27 11:02:16 +01: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', | ||||
|                 justifyContent: 'space-between', | ||||
|                 alignItems: 'center', | ||||
|                 margin: theme.spacing(2, 1, 0, 2), | ||||
|                 margin: theme.spacing(2, 1, 0, mode === 'mini' ? 1 : 2), | ||||
|             })} | ||||
|         > | ||||
|             {mode === 'full' && ( | ||||
|  | ||||
| @ -2,7 +2,14 @@ import { useState, type VFC } from 'react'; | ||||
| import useMediaQuery from '@mui/material/useMediaQuery'; | ||||
| import { useTheme } from '@mui/material/styles'; | ||||
| 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 UserProfile from 'component/user/UserProfile'; | ||||
| import { ConditionallyRender } from 'component/common/ConditionallyRender/ConditionallyRender'; | ||||
| @ -226,6 +233,12 @@ const Header: VFC = () => { | ||||
|                                 <MenuBookIcon /> | ||||
|                             </StyledIconButton> | ||||
|                         </Tooltip> | ||||
|                         <Divider | ||||
|                             orientation='vertical' | ||||
|                             variant='middle' | ||||
|                             flexItem | ||||
|                             sx={{ ml: 1 }} | ||||
|                         /> | ||||
|                         <UserProfile /> | ||||
|                     </StyledUserContainer> | ||||
|                 </StyledNav> | ||||
|  | ||||
| @ -1,38 +1,43 @@ | ||||
| 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 type { IUser } from 'interfaces/user'; | ||||
| import { HEADER_USER_AVATAR } from 'utils/testIds'; | ||||
| import { useId } from 'hooks/useId'; | ||||
| import { UserAvatar } from 'component/common/UserAvatar/UserAvatar'; | ||||
| import { flexRow, itemsCenter } from 'themes/themeStyles'; | ||||
| 
 | ||||
| const StyledUserAvatar = styled(UserAvatar)(({ theme }) => ({ | ||||
|     width: theme.spacing(4.5), | ||||
|     height: theme.spacing(4.5), | ||||
| })); | ||||
| import ExpandMoreIcon from '@mui/icons-material/ExpandMore'; | ||||
| import ExpandLessIcon from '@mui/icons-material/ExpandLess'; | ||||
| import { HEADER_USER_AVATAR } from 'utils/testIds'; | ||||
| 
 | ||||
| const StyledProfileContainer = styled('div')(({ theme }) => ({ | ||||
|     position: 'relative', | ||||
| })); | ||||
| 
 | ||||
| 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%', | ||||
|     }, | ||||
|     marginLeft: theme.spacing(2), | ||||
|     cursor: 'pointer', | ||||
| })); | ||||
| 
 | ||||
| interface IUserProfileProps { | ||||
|     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 [showProfile, setShowProfile] = useState(false); | ||||
|     const modalId = useId(); | ||||
| @ -40,20 +45,28 @@ const UserProfile = ({ profile }: IUserProfileProps) => { | ||||
|     return ( | ||||
|         <ClickAwayListener onClickAway={() => setShowProfile(false)}> | ||||
|             <StyledProfileContainer> | ||||
|                 <Tooltip title='Profile' arrow> | ||||
|                     <StyledIconButton | ||||
|                         onClick={() => setShowProfile((prev) => !prev)} | ||||
|                         aria-controls={showProfile ? modalId : undefined} | ||||
|                         aria-expanded={showProfile} | ||||
|                         color='secondary' | ||||
|                         size='large' | ||||
|                     > | ||||
|                         <StyledUserAvatar | ||||
|                             user={profile} | ||||
|                             data-testid={HEADER_USER_AVATAR} | ||||
|                         /> | ||||
|                     </StyledIconButton> | ||||
|                 </Tooltip> | ||||
|                 <Button | ||||
|                     component={Box} | ||||
|                     sx={{ display: 'flex', alignItems: 'center' }} | ||||
|                     aria-controls={showProfile ? modalId : undefined} | ||||
|                     aria-expanded={showProfile} | ||||
|                     onClick={() => setShowProfile((prev) => !prev)} | ||||
|                 > | ||||
|                     <StyledUserAvatar | ||||
|                         user={profile} | ||||
|                         data-testid={HEADER_USER_AVATAR} | ||||
|                     /> | ||||
|                     <Box sx={{ mr: 3 }}> | ||||
|                         <Typography> | ||||
|                             {profile.name || profile.username} | ||||
|                         </Typography> | ||||
|                         <StyledSubtitle variant='body2' title={profile.email}> | ||||
|                             {profile.email} | ||||
|                         </StyledSubtitle> | ||||
|                     </Box> | ||||
|                     {showProfile ? <ExpandLessIcon /> : <ExpandMoreIcon />} | ||||
|                 </Button> | ||||
| 
 | ||||
|                 <UserProfileContent | ||||
|                     id={modalId} | ||||
|                     showProfile={showProfile} | ||||
|  | ||||
| @ -1,10 +1,9 @@ | ||||
| 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 type { IUser } from 'interfaces/user'; | ||||
| import OpenInNew from '@mui/icons-material/OpenInNew'; | ||||
| import { Link as RouterLink } from 'react-router-dom'; | ||||
| import { UserAvatar } from 'component/common/UserAvatar/UserAvatar'; | ||||
| 
 | ||||
| const StyledPaper = styled(Paper)(({ theme }) => ({ | ||||
|     display: 'flex', | ||||
| @ -15,35 +14,15 @@ const StyledPaper = styled(Paper)(({ theme }) => ({ | ||||
|     boxShadow: theme.boxShadows.popup, | ||||
|     position: 'absolute', | ||||
|     zIndex: 5000, | ||||
|     minWidth: theme.spacing(37.5), | ||||
|     minWidth: theme.spacing(34), | ||||
|     right: 0, | ||||
|     marginTop: theme.spacing(0.25), | ||||
|     [theme.breakpoints.down('md')]: { | ||||
|         width: '100%', | ||||
|         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 }) => ({ | ||||
|     display: 'flex', | ||||
|     alignItems: 'center', | ||||
| @ -88,18 +67,6 @@ export const UserProfileContent = ({ | ||||
|         condition={showProfile} | ||||
|         show={ | ||||
|             <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 | ||||
|                     component={RouterLink} | ||||
|                     to='/profile' | ||||
|  | ||||
		Loading…
	
		Reference in New Issue
	
	Block a user