Skip to content

Commit

Permalink
fix: use radix-ui for avatars (#9633)
Browse files Browse the repository at this point in the history
Signed-off-by: Matt Krick <matt.krick@gmail.com>
  • Loading branch information
mattkrick committed Apr 11, 2024
1 parent e4f8d49 commit 38c8e61
Show file tree
Hide file tree
Showing 34 changed files with 159 additions and 374 deletions.
3 changes: 2 additions & 1 deletion .prettierrc
Original file line number Diff line number Diff line change
Expand Up @@ -13,5 +13,6 @@
"singleQuote": false
}
}
]
],
"plugins": ["prettier-plugin-tailwindcss"]
}
26 changes: 2 additions & 24 deletions .vscode/settings.json
Original file line number Diff line number Diff line change
@@ -1,30 +1,7 @@
{
"git.ignoreLimitWarning": true,
"eslint.packageManager": "yarn",
"typescript.tsdk": "node_modules/typescript/lib",
"editor.tabSize": 2,
"eslint.workingDirectories": [
{
"directory": "./packages/server",
"changeProcessCWD": true
},
{
"directory": "./packages/client",
"changeProcessCWD": true
}
],
"eslint.validate": [
"javascript",
"javascriptreact",
{
"language": "typescript",
"autoFix": true
},
{
"language": "typescriptreact",
"autoFix": true
}
],
"npm.packageManager": "yarn",
"editor.codeActionsOnSave": {
"source.fixAll.eslint": "explicit"
Expand Down Expand Up @@ -53,5 +30,6 @@
"**/graphql/private/schema.graphql": true,
"**/graphql/public/schema.graphql": true,
"**/resolverTypes.ts": true
}
},
"tailwindCSS.experimental.classRegex": [["clsx\\(([^)]*)\\)", "(?:'|\"|`)([^']*)(?:'|\"|`)"]]
}
2 changes: 1 addition & 1 deletion packages/client/components/ActionMeetingAgendaItems.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -93,7 +93,7 @@ const ActionMeetingAgendaItems = (props: Props) => {
</MeetingTopBar>
<PhaseWrapper>
<AgendaVerbatim>
<Avatar picture={picture} size={64} />
<Avatar picture={picture} className={'h-16 w-16'} />
<StyledHeading>{content}</StyledHeading>
</AgendaVerbatim>
<StyledCopy>{`${preferredName}, what do you need?`}</StyledCopy>
Expand Down
5 changes: 2 additions & 3 deletions packages/client/components/ActionMeetingUpdatesPrompt.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -2,9 +2,8 @@ import styled from '@emotion/styled'
import graphql from 'babel-plugin-relay/macro'
import React from 'react'
import {useFragment} from 'react-relay'
import ActionMeetingUpdatesPromptTeamHelpText from '../modules/meeting/components/ActionMeetingUpdatesPromptTeamHelpText'
import defaultUserAvatar from '../styles/theme/images/avatar-user.svg'
import {ActionMeetingUpdatesPrompt_meeting$key} from '../__generated__/ActionMeetingUpdatesPrompt_meeting.graphql'
import ActionMeetingUpdatesPromptTeamHelpText from '../modules/meeting/components/ActionMeetingUpdatesPromptTeamHelpText'
import Avatar from './Avatar/Avatar'
import PhaseHeaderDescription from './PhaseHeaderDescription'
import PhaseHeaderTitle from './PhaseHeaderTitle'
Expand Down Expand Up @@ -89,7 +88,7 @@ const ActionMeetingUpdatesPrompt = (props: Props) => {
const taskCount = tasks.edges.length
return (
<StyledPrompt>
<Avatar picture={picture || defaultUserAvatar} size={64} />
<Avatar picture={picture} className={'h-16 w-16'} />
<PromptText>
<StyledHeader className='max-w-full'>
{prefix}
Expand Down
94 changes: 18 additions & 76 deletions packages/client/components/Avatar/Avatar.tsx
Original file line number Diff line number Diff line change
@@ -1,92 +1,34 @@
import styled from '@emotion/styled'
import React, {forwardRef, useState} from 'react'
import clsx from 'clsx'
import React, {forwardRef} from 'react'
import defaultUserAvatar from '../../styles/theme/images/avatar-user.svg'
import AvatarBadge from '../AvatarBadge/AvatarBadge'

type ImageBlockProps = Pick<Props, 'sansRadius' | 'sansShadow' | 'picture' | 'size' | 'onClick'>

const ImageBlock = styled('div')<ImageBlockProps>(
({sansRadius, sansShadow, picture, size, onClick}) => ({
backgroundImage: `url(${picture})`,
backgroundPosition: 'center center',
backgroundRepeat: 'no-repeat',
backgroundSize: 'cover',
borderRadius: sansRadius ? 0 : '100%',
boxShadow: sansShadow ? 'none' : undefined,
cursor: onClick ? 'pointer' : 'default',
display: 'block',
flexShrink: 0,
width: size,
height: size
})
)

const BadgeBlock = styled('div')({
alignItems: 'center',
display: 'flex',
height: '25%',
justifyContent: 'center',
position: 'absolute',
right: 0,
top: 0,
width: '25%'
})

const BadgeBlockInner = styled('div')({
flexShrink: 0
})
import {Avatar as AvatarRoot} from '../../ui/Avatar/Avatar'
import {AvatarFallback} from '../../ui/Avatar/AvatarFallback'
import {AvatarImage} from '../../ui/Avatar/AvatarImage'

interface Props {
alt?: string
className?: string
hasBadge?: boolean
isConnected?: boolean
onClick?: (e?: React.MouseEvent) => void
onMouseEnter?: () => void
onTransitionEnd?: () => void
picture: string
sansRadius?: boolean
sansShadow?: boolean
size: number
picture?: string | null
}

const Avatar = forwardRef((props: Props, ref: any) => {
const {
className,
hasBadge,
isConnected,
onClick,
onMouseEnter,
onTransitionEnd,
picture,
sansRadius,
sansShadow,
size
} = props
const [imageUrl, setImageUrl] = useState(picture || defaultUserAvatar)
const onError = () => {
setImageUrl(defaultUserAvatar)
}
const Avatar = forwardRef<HTMLDivElement, Props>((props, ref) => {
const {alt, className, onClick, onTransitionEnd, onMouseEnter, picture} = props
return (
<ImageBlock
onTransitionEnd={onTransitionEnd}
className={className}
ref={ref}
<AvatarRoot
onClick={onClick}
onTransitionEnd={onTransitionEnd}
onMouseEnter={onMouseEnter}
sansRadius={sansRadius}
sansShadow={sansShadow}
picture={imageUrl}
size={size}
ref={ref}
className={clsx(`${onClick && 'cursor-pointer'}`, className)}
>
<img src={imageUrl} className='hidden' onError={onError} />
{hasBadge && (
<BadgeBlock>
<BadgeBlockInner>
<AvatarBadge isConnected={isConnected || false} />
</BadgeBlockInner>
</BadgeBlock>
)}
</ImageBlock>
<AvatarImage src={picture || defaultUserAvatar} alt={alt || 'Avatar'} />
<AvatarFallback>
<img src={defaultUserAvatar} alt={alt || 'Avatar not found'} />
</AvatarFallback>
</AvatarRoot>
)
})

Expand Down
4 changes: 2 additions & 2 deletions packages/client/components/AvatarList.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -3,10 +3,10 @@ import graphql from 'babel-plugin-relay/macro'
import React, {ReactElement, useLayoutEffect, useRef, useState} from 'react'
import {useFragment} from 'react-relay'
import useResizeObserver from '~/hooks/useResizeObserver'
import {AvatarList_users$key} from '../__generated__/AvatarList_users.graphql'
import useOverflowAvatars from '../hooks/useOverflowAvatars'
import {TransitionStatus} from '../hooks/useTransition'
import {BezierCurve} from '../types/constEnums'
import {AvatarList_users$key} from '../__generated__/AvatarList_users.graphql'
import AvatarListUser from './AvatarListUser'
import OverflowAvatar from './OverflowAvatar'

Expand Down Expand Up @@ -117,7 +117,7 @@ const AvatarList = (props: Props) => {
onTransitionEnd={onTransitionEnd}
status={status}
offset={offsetSize * displayIdx}
width={size}
className={`${size === 28 ? 'h-7 w-7' : size === 46 ? 'h-[46px] w-[46px]' : ''}`}
borderColor={borderColor}
/>
)
Expand Down
40 changes: 10 additions & 30 deletions packages/client/components/AvatarListUser.tsx
Original file line number Diff line number Diff line change
@@ -1,12 +1,13 @@
import styled from '@emotion/styled'
import graphql from 'babel-plugin-relay/macro'
import clsx from 'clsx'
import React from 'react'
import {useFragment} from 'react-relay'
import {TransitionStatus} from '~/hooks/useTransition'
import {AvatarListUser_user$key} from '../__generated__/AvatarListUser_user.graphql'
import {MenuPosition} from '../hooks/useCoords'
import useTooltip from '../hooks/useTooltip'
import {BezierCurve} from '../types/constEnums'
import {AvatarListUser_user$key} from '../__generated__/AvatarListUser_user.graphql'
import Avatar from './Avatar/Avatar'

const Wrapper = styled('div')<{offset: number; isColumn?: boolean}>(({offset, isColumn}) => ({
Expand All @@ -15,26 +16,6 @@ const Wrapper = styled('div')<{offset: number; isColumn?: boolean}>(({offset, is
transition: `all 300ms ${BezierCurve.DECELERATE}`
}))

const StyledAvatar = styled(Avatar)<{
status?: TransitionStatus
isAnimated: boolean
borderColor?: string
width: number
}>(({status, isAnimated, borderColor = '#fff', width}) => ({
border: `${width >= 40 ? '3px' : '2px'} solid ${borderColor}`,
opacity: !isAnimated
? undefined
: status === TransitionStatus.EXITING || status === TransitionStatus.MOUNTED
? 0
: 1,
transform: !isAnimated
? undefined
: status === TransitionStatus.EXITING || status === TransitionStatus.MOUNTED
? 'scale(0)'
: 'scale(1)',
transition: `all 300ms ${BezierCurve.DECELERATE}`
}))

interface Props {
className?: string
offset: number
Expand All @@ -43,7 +24,6 @@ interface Props {
onTransitionEnd?: () => void
status?: TransitionStatus
user: AvatarListUser_user$key
width: number
onClick?: () => void
borderColor?: string
}
Expand All @@ -57,7 +37,6 @@ const AvatarListUser = (props: Props) => {
status,
offset,
isAnimated,
width,
onClick,
borderColor
} = props
Expand All @@ -74,6 +53,9 @@ const AvatarListUser = (props: Props) => {
const {tooltipPortal, openTooltip, closeTooltip, originRef} = useTooltip<HTMLDivElement>(
MenuPosition.UPPER_CENTER
)
const isAnimating =
isAnimated && (status === TransitionStatus.EXITING || status === TransitionStatus.MOUNTED)

return (
<Wrapper
ref={originRef}
Expand All @@ -83,15 +65,13 @@ const AvatarListUser = (props: Props) => {
onMouseOver={openTooltip}
onMouseLeave={closeTooltip}
>
<StyledAvatar
className={className}
status={status}
<Avatar
className={clsx(
`border-solid border-[${borderColor || '#fff'}] duration-300 ease-out ${isAnimating ? 'scale-0 opacity-0' : 'scale-100 opacity-100'}`,
className
)}
onTransitionEnd={onTransitionEnd}
picture={picture}
size={width}
isAnimated={isAnimated}
borderColor={borderColor}
width={width}
/>
{tooltipPortal(preferredName)}
</Wrapper>
Expand Down
26 changes: 4 additions & 22 deletions packages/client/components/DashboardAvatars/DashboardAvatar.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -2,15 +2,13 @@ import styled from '@emotion/styled'
import graphql from 'babel-plugin-relay/macro'
import React from 'react'
import {commitLocalUpdate, useFragment} from 'react-relay'
import {DashboardAvatar_teamMember$key} from '../../__generated__/DashboardAvatar_teamMember.graphql'
import useAtmosphere from '../../hooks/useAtmosphere'
import {MenuPosition} from '../../hooks/useCoords'
import useMutationProps from '../../hooks/useMutationProps'
import useTooltip from '../../hooks/useTooltip'
import ToggleTeamDrawerMutation from '../../mutations/ToggleTeamDrawerMutation'
import {PALETTE} from '../../styles/paletteV3'
import defaultUserAvatar from '../../styles/theme/images/avatar-user.svg'
import {ElementWidth} from '../../types/constEnums'
import {DashboardAvatar_teamMember$key} from '../../__generated__/DashboardAvatar_teamMember.graphql'
import Avatar from '../Avatar/Avatar'

interface Props {
Expand All @@ -21,20 +19,6 @@ const AvatarWrapper = styled('div')({
width: ElementWidth.DASHBOARD_AVATAR_OVERLAPPED
})

const StyledAvatar = styled(Avatar)<{isConnected: boolean; picture: string}>(
({isConnected, picture}) => ({
// opacity causes transparency making overlap look bad. use img instead
backgroundImage: `${
isConnected ? '' : 'linear-gradient(rgba(255,255,255,.65), rgba(255,255,255,.65)),'
} url(${picture}), url(${defaultUserAvatar})`,
border: `2px solid ${PALETTE.SLATE_200}`,
':hover': {
backgroundImage: `linear-gradient(rgba(255,255,255,.5), rgba(255,255,255,.5)),
url(${picture}), url(${defaultUserAvatar})`
}
})
)

const DashboardAvatar = (props: Props) => {
const {teamMember: teamMemberRef} = props
const teamMember = useFragment(
Expand Down Expand Up @@ -86,13 +70,11 @@ const DashboardAvatar = (props: Props) => {

return (
<AvatarWrapper onMouseEnter={openTooltip} onMouseLeave={closeTooltip}>
<StyledAvatar
{...teamMember}
isConnected={!!isConnected}
<Avatar
onClick={handleClick}
picture={picture || defaultUserAvatar}
picture={picture}
ref={originRef}
size={ElementWidth.DASHBOARD_AVATAR}
className={`h-7 w-7 border-2 border-solid border-slate-200 after:absolute after:h-full after:w-full after:content-[""] hover:after:bg-white/30 ${!isConnected && 'after:bg-white/60'}`}
/>
{tooltipPortal(preferredName)}
</AvatarWrapper>
Expand Down
16 changes: 3 additions & 13 deletions packages/client/components/DeckActivityAvatars.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -2,10 +2,10 @@ import styled from '@emotion/styled'
import graphql from 'babel-plugin-relay/macro'
import React, {useMemo} from 'react'
import {useFragment} from 'react-relay'
import {DeckActivityAvatars_stage$key} from '../__generated__/DeckActivityAvatars_stage.graphql'
import useAtmosphere from '../hooks/useAtmosphere'
import useTransition, {TransitionStatus} from '../hooks/useTransition'
import {PokerCards} from '../types/constEnums'
import {DeckActivityAvatars_stage$key} from '../__generated__/DeckActivityAvatars_stage.graphql'
import AvatarListUser from './AvatarListUser'

const DeckActivityPanel = styled('div')({
Expand All @@ -19,16 +19,6 @@ const DeckActivityPanel = styled('div')({
zIndex: 100 // show above dimension column
})

const PeekingAvatar = styled(AvatarListUser)<{status?: TransitionStatus}>(({status}) => ({
opacity: status === TransitionStatus.EXITING ? 0 : 1,
transform:
status === TransitionStatus.MOUNTED
? `translate(64px)`
: status === TransitionStatus.EXITING
? 'scale(0)'
: undefined
}))

interface Props {
stage: DeckActivityAvatars_stage$key
}
Expand Down Expand Up @@ -78,15 +68,15 @@ const DeckActivityAvatars = (props: Props) => {
const visibleScoreIdx = peekingUsers.findIndex((user) => user.id === userId)
const displayIdx = visibleScoreIdx === -1 ? idx : visibleScoreIdx
return (
<PeekingAvatar
<AvatarListUser
key={userId}
status={status}
onTransitionEnd={onTransitionEnd}
user={child}
offset={(PokerCards.AVATAR_WIDTH - 10) * displayIdx}
isColumn
isAnimated
width={PokerCards.AVATAR_WIDTH as number}
className={`h-[46px] w-[46px] border-[3px] opacity-100 ${status === TransitionStatus.EXITING ? 'scale-0 opacity-0' : status === TransitionStatus.MOUNTED ? 'translate-x-64' : ''}`}
/>
)
})}
Expand Down
Loading

0 comments on commit 38c8e61

Please sign in to comment.