Extend Avatar (#371)
* add color hash to Avatar * remove compoundVariants * remove outline prop * remove vars * ref figma * remove example * add background color to Image * extend radius variants in Image * use union type * add channel avatar to stories * add channel avatar as Avatar type * resolve typecheck errors * add name prop * add icon avatar as Avatar type * add community avatar * move fallback * set default icon color * add group avatar * add wallet avatar * join user type * join channel type * remove 32 text variant * assert LockBase variant * remove fn type guards * fix icon import * set icon sizes based on props * set default variants and use render fns * uses raidus tokens * add outline * remove outline * fix overlapping background on loaded image * fix indicator position * fix background color
This commit is contained in:
parent
5048d7286a
commit
6fa65c8ee6
|
@ -1,64 +1,382 @@
|
|||
import { PlaceholderIcon } from '@status-im/icons'
|
||||
import { Stack } from '@tamagui/core'
|
||||
|
||||
import { Avatar } from './avatar'
|
||||
|
||||
import type {
|
||||
AccountAvatarProps,
|
||||
ChannelAvatarProps,
|
||||
CommunityAvatarProps,
|
||||
GroupAvatarProps,
|
||||
IconAvatarProps,
|
||||
UserAvatarProps,
|
||||
WalletAvatarProps,
|
||||
} from './avatar'
|
||||
import type { Meta, StoryObj } from '@storybook/react'
|
||||
|
||||
// More on how to set up stories at: https://storybook.js.org/docs/7.0/react/writing-stories/introduction
|
||||
const meta: Meta<typeof Avatar> = {
|
||||
component: Avatar,
|
||||
argTypes: {},
|
||||
parameters: {
|
||||
design: {
|
||||
type: 'figma',
|
||||
url: 'https://www.figma.com/file/IBmFKgGL1B4GzqD8LQTw6n/Design-System-for-Desktop%2FWeb?node-id=102-5246&t=i4haPXGOeNtaLaEz-0',
|
||||
},
|
||||
},
|
||||
}
|
||||
|
||||
type Story = StoryObj<typeof Avatar>
|
||||
type UserArgs = Pick<UserAvatarProps, 'type' | 'src' | 'name'>
|
||||
|
||||
// More on writing stories with args: https://storybook.js.org/docs/7.0/react/writing-stories/args
|
||||
export const Default: Story = {
|
||||
export const User: StoryObj<UserArgs> = {
|
||||
// todo?: https://github.com/storybookjs/storybook/issues/13747
|
||||
args: {
|
||||
type: 'user',
|
||||
name: 'John Doe',
|
||||
src: 'https://images.unsplash.com/photo-1518020382113-a7e8fc38eac9?ixlib=rb-4.0.3&ixid=MnwxMjA3fDB8MHxwaG90by1wYWdlfHx8fGVufDB8fHx8&auto=format&fit=crop&w=500&h=500&q=80',
|
||||
} as UserArgs,
|
||||
parameters: {
|
||||
design: {
|
||||
type: 'figma',
|
||||
url: 'https://www.figma.com/file/IBmFKgGL1B4GzqD8LQTw6n/Design-System-for-Desktop%2FWeb?node-id=115-6787&t=kcsW0DN5ochMPO1u-4',
|
||||
},
|
||||
},
|
||||
render: args => (
|
||||
<Stack space flexDirection="row">
|
||||
<Stack space>
|
||||
<Avatar {...args} size={80} />
|
||||
<Avatar {...args} size={56} />
|
||||
<Avatar {...args} size={48} />
|
||||
<Avatar {...args} size={32} />
|
||||
<Avatar {...args} size={28} />
|
||||
<Avatar {...args} size={24} />
|
||||
<Avatar {...args} size={20} />
|
||||
<Avatar {...args} size={16} />
|
||||
<Stack space flexDirection="column">
|
||||
<Stack space alignItems="flex-start">
|
||||
<Avatar
|
||||
{...args}
|
||||
size={80}
|
||||
indicator="online"
|
||||
colorHash={[
|
||||
[3, 30],
|
||||
[2, 10],
|
||||
[5, 5],
|
||||
[3, 14],
|
||||
[5, 4],
|
||||
[4, 19],
|
||||
[3, 16],
|
||||
[4, 0],
|
||||
[5, 28],
|
||||
[4, 13],
|
||||
[4, 15],
|
||||
]}
|
||||
/>
|
||||
<Avatar
|
||||
{...args}
|
||||
size={56}
|
||||
indicator="online"
|
||||
colorHash={[
|
||||
[3, 30],
|
||||
[2, 10],
|
||||
[5, 5],
|
||||
[3, 14],
|
||||
[5, 4],
|
||||
[4, 19],
|
||||
[3, 16],
|
||||
[4, 0],
|
||||
[5, 28],
|
||||
[4, 13],
|
||||
[4, 15],
|
||||
]}
|
||||
/>
|
||||
<Avatar
|
||||
{...args}
|
||||
size={48}
|
||||
indicator="online"
|
||||
colorHash={[
|
||||
[3, 30],
|
||||
[2, 10],
|
||||
[5, 5],
|
||||
[3, 14],
|
||||
[5, 4],
|
||||
[4, 19],
|
||||
[3, 16],
|
||||
[4, 0],
|
||||
[5, 28],
|
||||
[4, 13],
|
||||
[4, 15],
|
||||
]}
|
||||
/>
|
||||
<Avatar
|
||||
{...args}
|
||||
size={32}
|
||||
indicator="online"
|
||||
colorHash={[
|
||||
[3, 30],
|
||||
[2, 10],
|
||||
[5, 5],
|
||||
[3, 14],
|
||||
[5, 4],
|
||||
[4, 19],
|
||||
[3, 16],
|
||||
[4, 0],
|
||||
[5, 28],
|
||||
[4, 13],
|
||||
[4, 15],
|
||||
]}
|
||||
/>
|
||||
</Stack>
|
||||
<Stack space alignItems="flex-start">
|
||||
<Avatar {...args} size={80} indicator="online" />
|
||||
<Avatar {...args} size={56} indicator="online" />
|
||||
<Avatar {...args} size={48} indicator="online" />
|
||||
<Avatar {...args} size={32} indicator="online" />
|
||||
<Avatar {...args} size={28} indicator="online" />
|
||||
<Avatar {...args} size={24} indicator="online" />
|
||||
</Stack>
|
||||
<Stack space alignItems="flex-start">
|
||||
<Avatar {...args} size={80} />
|
||||
<Avatar {...args} size={56} />
|
||||
<Avatar {...args} size={48} />
|
||||
<Avatar {...args} size={32} />
|
||||
<Avatar {...args} size={28} />
|
||||
<Avatar {...args} size={24} />
|
||||
<Avatar {...args} size={20} />
|
||||
<Avatar {...args} size={16} />
|
||||
</Stack>
|
||||
</Stack>
|
||||
|
||||
<Stack space>
|
||||
<Avatar {...args} size={80} indicator="online" />
|
||||
<Avatar {...args} size={56} indicator="online" />
|
||||
<Avatar {...args} size={48} indicator="online" />
|
||||
<Avatar {...args} size={32} indicator="online" />
|
||||
<Avatar {...args} size={28} indicator="online" />
|
||||
<Avatar {...args} size={24} indicator="online" />
|
||||
<Avatar {...args} size={20} indicator="online" />
|
||||
<Avatar {...args} size={16} indicator="online" />
|
||||
<Stack space flexDirection="column">
|
||||
<Stack space alignItems="flex-start">
|
||||
<Avatar
|
||||
{...args}
|
||||
src={undefined}
|
||||
size={80}
|
||||
indicator="online"
|
||||
colorHash={[
|
||||
[3, 30],
|
||||
[2, 10],
|
||||
[5, 5],
|
||||
[3, 14],
|
||||
[5, 4],
|
||||
[4, 19],
|
||||
[3, 16],
|
||||
[4, 0],
|
||||
[5, 28],
|
||||
[4, 13],
|
||||
[4, 15],
|
||||
]}
|
||||
/>
|
||||
<Avatar
|
||||
{...args}
|
||||
src={undefined}
|
||||
size={56}
|
||||
indicator="online"
|
||||
colorHash={[
|
||||
[3, 30],
|
||||
[2, 10],
|
||||
[5, 5],
|
||||
[3, 14],
|
||||
[5, 4],
|
||||
[4, 19],
|
||||
[3, 16],
|
||||
[4, 0],
|
||||
[5, 28],
|
||||
[4, 13],
|
||||
[4, 15],
|
||||
]}
|
||||
/>
|
||||
<Avatar
|
||||
{...args}
|
||||
src={undefined}
|
||||
size={48}
|
||||
indicator="online"
|
||||
colorHash={[
|
||||
[3, 30],
|
||||
[2, 10],
|
||||
[5, 5],
|
||||
[3, 14],
|
||||
[5, 4],
|
||||
[4, 19],
|
||||
[3, 16],
|
||||
[4, 0],
|
||||
[5, 28],
|
||||
[4, 13],
|
||||
[4, 15],
|
||||
]}
|
||||
/>
|
||||
<Avatar
|
||||
{...args}
|
||||
src={undefined}
|
||||
size={32}
|
||||
indicator="online"
|
||||
colorHash={[
|
||||
[3, 30],
|
||||
[2, 10],
|
||||
[5, 5],
|
||||
[3, 14],
|
||||
[5, 4],
|
||||
[4, 19],
|
||||
[3, 16],
|
||||
[4, 0],
|
||||
[5, 28],
|
||||
[4, 13],
|
||||
[4, 15],
|
||||
]}
|
||||
/>
|
||||
</Stack>
|
||||
</Stack>
|
||||
</Stack>
|
||||
),
|
||||
}
|
||||
|
||||
export const Rounded: Story = {
|
||||
export const Group: StoryObj<GroupAvatarProps> = {
|
||||
args: {
|
||||
type: 'group',
|
||||
src: 'https://images.unsplash.com/photo-1518020382113-a7e8fc38eac9?ixlib=rb-4.0.3&ixid=MnwxMjA3fDB8MHxwaG90by1wYWdlfHx8fGVufDB8fHx8&auto=format&fit=crop&w=500&h=500&q=80',
|
||||
shape: 'rounded',
|
||||
},
|
||||
parameters: {
|
||||
design: {
|
||||
type: 'figma',
|
||||
url: 'https://www.figma.com/file/IBmFKgGL1B4GzqD8LQTw6n/Design-System-for-Desktop%2FWeb?node-id=14584-169312&t=kcsW0DN5ochMPO1u-4',
|
||||
},
|
||||
},
|
||||
render: args => (
|
||||
<Stack space flexDirection="row">
|
||||
<Stack space flexDirection="column">
|
||||
<Avatar {...args} size={80} />
|
||||
<Avatar {...args} size={48} />
|
||||
<Avatar {...args} size={32} />
|
||||
<Avatar {...args} size={28} />
|
||||
<Avatar {...args} size={20} />
|
||||
</Stack>
|
||||
<Stack space flexDirection="column">
|
||||
<Avatar {...args} src={undefined} size={80} />
|
||||
<Avatar {...args} src={undefined} size={48} />
|
||||
<Avatar {...args} src={undefined} size={32} />
|
||||
<Avatar {...args} src={undefined} size={28} />
|
||||
<Avatar {...args} src={undefined} size={20} />
|
||||
</Stack>
|
||||
</Stack>
|
||||
),
|
||||
}
|
||||
|
||||
export const Wallet: StoryObj<WalletAvatarProps> = {
|
||||
args: {
|
||||
type: 'wallet',
|
||||
name: 'Wallet 1',
|
||||
},
|
||||
parameters: {
|
||||
design: {
|
||||
type: 'figma',
|
||||
url: 'https://www.figma.com/file/IBmFKgGL1B4GzqD8LQTw6n/Design-System-for-Desktop%2FWeb?node-id=114-7646&t=kcsW0DN5ochMPO1u-4',
|
||||
},
|
||||
},
|
||||
render: args => (
|
||||
<Stack space flexDirection="row">
|
||||
<Stack space flexDirection="column">
|
||||
<Avatar {...args} size={80} />
|
||||
<Avatar {...args} size={48} />
|
||||
<Avatar {...args} size={32} />
|
||||
<Avatar {...args} size={28} />
|
||||
<Avatar {...args} size={20} />
|
||||
</Stack>
|
||||
</Stack>
|
||||
),
|
||||
}
|
||||
|
||||
export const Account: StoryObj<AccountAvatarProps> = {
|
||||
args: {
|
||||
type: 'account',
|
||||
name: 'My Account',
|
||||
src: 'https://images.unsplash.com/photo-1518020382113-a7e8fc38eac9?ixlib=rb-4.0.3&ixid=MnwxMjA3fDB8MHxwaG90by1wYWdlfHx8fGVufDB8fHx8&auto=format&fit=crop&w=500&h=500&q=80',
|
||||
},
|
||||
parameters: {
|
||||
design: {
|
||||
type: 'figma',
|
||||
url: 'https://www.figma.com/file/IBmFKgGL1B4GzqD8LQTw6n/Design-System-for-Desktop%2FWeb?node-id=483-19401&t=kcsW0DN5ochMPO1u-4',
|
||||
},
|
||||
},
|
||||
render: args => (
|
||||
<Stack space>
|
||||
<Avatar {...args} size={80} />
|
||||
<Avatar {...args} size={56} />
|
||||
<Avatar {...args} size={48} />
|
||||
<Avatar {...args} size={32} />
|
||||
<Avatar {...args} size={28} />
|
||||
<Avatar {...args} size={24} />
|
||||
<Avatar {...args} size={20} />
|
||||
<Avatar {...args} size={16} />
|
||||
</Stack>
|
||||
),
|
||||
}
|
||||
|
||||
export const community: StoryObj<CommunityAvatarProps> = {
|
||||
args: {
|
||||
type: 'community',
|
||||
src: 'https://images.unsplash.com/photo-1518020382113-a7e8fc38eac9?ixlib=rb-4.0.3&ixid=MnwxMjA3fDB8MHxwaG90by1wYWdlfHx8fGVufDB8fHx8&auto=format&fit=crop&w=500&h=500&q=80',
|
||||
},
|
||||
parameters: {
|
||||
design: {
|
||||
type: 'figma',
|
||||
url: 'https://www.figma.com/file/IBmFKgGL1B4GzqD8LQTw6n/Design-System-for-Desktop%2FWeb?node-id=8824-149725&t=kcsW0DN5ochMPO1u-4',
|
||||
},
|
||||
},
|
||||
render: args => (
|
||||
<Stack space flexDirection="row">
|
||||
<Stack space alignItems="flex-start">
|
||||
<Avatar {...args} size={32} />
|
||||
<Avatar {...args} size={24} />
|
||||
<Avatar {...args} size={20} />
|
||||
</Stack>
|
||||
</Stack>
|
||||
),
|
||||
}
|
||||
|
||||
type ChannelArgs = Pick<ChannelAvatarProps, 'type' | 'emoji'>
|
||||
|
||||
export const Channel: StoryObj<ChannelArgs> = {
|
||||
args: {
|
||||
type: 'channel',
|
||||
emoji: '🍑',
|
||||
} as ChannelArgs,
|
||||
parameters: {
|
||||
design: {
|
||||
type: 'figma',
|
||||
url: 'https://www.figma.com/file/IBmFKgGL1B4GzqD8LQTw6n/Design-System-for-Desktop%2FWeb?node-id=399-20709&t=kcsW0DN5ochMPO1u-4',
|
||||
},
|
||||
},
|
||||
render: args => (
|
||||
<Stack space flexDirection="row">
|
||||
<Stack space alignItems="flex-start">
|
||||
<Avatar {...args} size={80} />
|
||||
<Avatar {...args} size={32} />
|
||||
<Avatar {...args} size={24} />
|
||||
<Avatar {...args} size={20} />
|
||||
</Stack>
|
||||
|
||||
<Stack space alignItems="flex-start">
|
||||
<Avatar {...args} size={32} lock="locked" />
|
||||
<Avatar {...args} size={24} lock="locked" />
|
||||
<Avatar {...args} size={20} lock="locked" />
|
||||
</Stack>
|
||||
|
||||
<Stack space alignItems="flex-start">
|
||||
<Avatar {...args} size={32} lock="unlocked" />
|
||||
<Avatar {...args} size={24} lock="unlocked" />
|
||||
<Avatar {...args} size={20} lock="unlocked" />
|
||||
</Stack>
|
||||
</Stack>
|
||||
),
|
||||
}
|
||||
|
||||
export const Icon: StoryObj<IconAvatarProps> = {
|
||||
args: {
|
||||
type: 'icon',
|
||||
},
|
||||
parameters: {
|
||||
design: {
|
||||
type: 'figma',
|
||||
url: 'https://www.figma.com/file/IBmFKgGL1B4GzqD8LQTw6n/Design-System-for-Desktop%2FWeb?node-id=2931-44944&t=kcsW0DN5ochMPO1u-4',
|
||||
},
|
||||
},
|
||||
render: args => (
|
||||
<Stack space flexDirection="row">
|
||||
<Stack space alignItems="flex-start">
|
||||
<Avatar {...args} size={48} icon={<PlaceholderIcon size={20} />} />
|
||||
<Avatar {...args} size={32} icon={<PlaceholderIcon size={16} />} />
|
||||
<Avatar {...args} size={20} icon={<PlaceholderIcon size={12} />} />
|
||||
</Stack>
|
||||
</Stack>
|
||||
),
|
||||
}
|
||||
|
|
|
@ -1,72 +1,354 @@
|
|||
import { useEffect, useState } from 'react'
|
||||
import { cloneElement, useMemo, useState } from 'react'
|
||||
|
||||
import { Stack, styled, Text, Unspaced } from '@tamagui/core'
|
||||
import { LockedIcon, MembersIcon, UnlockedIcon } from '@status-im/icons'
|
||||
import { Stack, styled, Unspaced } from '@tamagui/core'
|
||||
import { Platform } from 'react-native'
|
||||
|
||||
import { Image } from '../image'
|
||||
import { Text } from '../text'
|
||||
import { tokens } from '../tokens'
|
||||
import { generateIdenticonRing } from './utils'
|
||||
|
||||
import type { GetStyledVariants } from '@tamagui/core'
|
||||
import type { TextProps } from '../text'
|
||||
import type { RadiusTokens } from '../tokens'
|
||||
import type { IconProps } from '@status-im/icons'
|
||||
import type { ColorTokens, GetStyledVariants } from '@tamagui/core'
|
||||
|
||||
type Variants = GetStyledVariants<typeof Base>
|
||||
|
||||
type Props = {
|
||||
src: string
|
||||
type UserAvatarProps = {
|
||||
type: 'user'
|
||||
size: 80 | 56 | 48 | 32 | 28 | 24 | 20 | 16
|
||||
shape?: Variants['shape']
|
||||
outline?: Variants['outline']
|
||||
name: string
|
||||
src?: string
|
||||
backgroundColor?: ColorTokens
|
||||
indicator?: GetStyledVariants<typeof Indicator>['state']
|
||||
colorHash?: number[][]
|
||||
}
|
||||
|
||||
type ImageLoadingStatus = 'idle' | 'loading' | 'loaded' | 'error'
|
||||
type GroupAvatarProps = {
|
||||
type: 'group'
|
||||
size: 80 | 48 | 32 | 28 | 20
|
||||
name: string
|
||||
src?: string
|
||||
backgroundColor?: ColorTokens
|
||||
}
|
||||
|
||||
const Avatar = (props: Props) => {
|
||||
const {
|
||||
src,
|
||||
size,
|
||||
shape = 'circle',
|
||||
outline = false,
|
||||
indicator = 'none',
|
||||
} = props
|
||||
type WalletAvatarProps = {
|
||||
type: 'wallet'
|
||||
size: 80 | 48 | 32 | 28 | 20
|
||||
name: string
|
||||
backgroundColor?: ColorTokens
|
||||
}
|
||||
|
||||
const [status, setStatus] = useState<ImageLoadingStatus>('idle')
|
||||
type ChannelAvatarProps = {
|
||||
type: 'channel'
|
||||
size: 80 | 32 | 24 | 20
|
||||
emoji: string
|
||||
backgroundColor?: ColorTokens
|
||||
background?: ColorTokens
|
||||
lock?: 'locked' | 'unlocked'
|
||||
}
|
||||
|
||||
useEffect(() => {
|
||||
setStatus('idle')
|
||||
}, [src])
|
||||
type CommunityAvatarProps = {
|
||||
type: 'community'
|
||||
size: 80 | 32 | 24 | 20
|
||||
name: string
|
||||
src?: string
|
||||
backgroundColor?: ColorTokens
|
||||
}
|
||||
|
||||
type AccountAvatarProps = {
|
||||
type: 'account'
|
||||
size: 80 | 48 | 32 | 28 | 24 | 20
|
||||
name: string
|
||||
src?: string
|
||||
backgroundColor?: ColorTokens
|
||||
}
|
||||
|
||||
type IconAvatarProps = {
|
||||
type: 'icon'
|
||||
size: 48 | 32 | 20
|
||||
icon: React.ReactElement
|
||||
backgroundColor?: ColorTokens
|
||||
color?: ColorTokens
|
||||
}
|
||||
|
||||
type AvatarProps =
|
||||
| UserAvatarProps
|
||||
| GroupAvatarProps
|
||||
| WalletAvatarProps
|
||||
| ChannelAvatarProps
|
||||
| CommunityAvatarProps
|
||||
| AccountAvatarProps
|
||||
| IconAvatarProps
|
||||
|
||||
type ImageLoadingStatus = 'loading' | 'loaded' | 'error'
|
||||
|
||||
const userPaddingSizes: Record<UserAvatarProps['size'], number> = {
|
||||
'80': 4,
|
||||
'56': 2,
|
||||
'48': 2,
|
||||
'32': 2,
|
||||
'28': 0,
|
||||
'24': 0,
|
||||
'20': 0,
|
||||
'16': 0,
|
||||
}
|
||||
|
||||
const accountRadiusSizes: Record<AccountAvatarProps['size'], RadiusTokens> = {
|
||||
'80': '$16',
|
||||
'48': '$12',
|
||||
'32': '$10',
|
||||
'28': '$8',
|
||||
'24': '$8',
|
||||
'20': '$6',
|
||||
}
|
||||
|
||||
const channelEmojiSizes: Record<ChannelAvatarProps['size'], TextProps['size']> =
|
||||
{
|
||||
// todo: design review
|
||||
'80': 27,
|
||||
'32': 15,
|
||||
'24': 13,
|
||||
'20': 11,
|
||||
}
|
||||
|
||||
const textSizes: Record<NonNullable<AvatarProps['size']>, TextProps['size']> = {
|
||||
'80': 27,
|
||||
'56': 19,
|
||||
'48': 19,
|
||||
'32': 15,
|
||||
'28': 13,
|
||||
'24': 13,
|
||||
'20': 11,
|
||||
'16': 11,
|
||||
}
|
||||
|
||||
const groupMembersIconSizes: Record<
|
||||
GroupAvatarProps['size'],
|
||||
IconProps['size'] | number // to scales SVG
|
||||
> = {
|
||||
// todo: design review
|
||||
'80': 36,
|
||||
'48': 20,
|
||||
'32': 16,
|
||||
'28': 16,
|
||||
'20': 12,
|
||||
}
|
||||
|
||||
const channelLockIconVariants: Record<
|
||||
ChannelAvatarProps['size'],
|
||||
{
|
||||
baseVariant: GetStyledVariants<typeof LockBase>['variant']
|
||||
iconSize: IconProps['size'] | number // to scales SVG
|
||||
}
|
||||
> = {
|
||||
// todo: design review
|
||||
'80': { baseVariant: 80, iconSize: 40 },
|
||||
'32': { baseVariant: 24, iconSize: 12 },
|
||||
'24': { baseVariant: 24, iconSize: 12 },
|
||||
'20': { baseVariant: 20, iconSize: 12 },
|
||||
}
|
||||
|
||||
const Avatar = (props: AvatarProps) => {
|
||||
const colorHash = 'colorHash' in props ? props.colorHash : undefined
|
||||
const identiconRing = useMemo(() => {
|
||||
if (colorHash) {
|
||||
const gradient = generateIdenticonRing(colorHash)
|
||||
return `conic-gradient(from 90deg, ${gradient})`
|
||||
}
|
||||
}, [colorHash])
|
||||
|
||||
const [status, setStatus] = useState<ImageLoadingStatus>()
|
||||
|
||||
const padding =
|
||||
props.type === 'user' && identiconRing ? userPaddingSizes[props.size] : 0
|
||||
const radius: RadiusTokens =
|
||||
props.type === 'account' ? accountRadiusSizes[props.size] : '$full'
|
||||
const backgroundColor = getBackgroundColor()
|
||||
|
||||
function getBackgroundColor(): ColorTokens {
|
||||
if ('src' in props && props.src) {
|
||||
switch (status) {
|
||||
case 'error':
|
||||
break
|
||||
case 'loaded':
|
||||
return '$transparent'
|
||||
case 'loading':
|
||||
default:
|
||||
return '$white-100'
|
||||
}
|
||||
}
|
||||
|
||||
if (props.backgroundColor) {
|
||||
return props.backgroundColor
|
||||
}
|
||||
|
||||
if (props.type === 'channel') {
|
||||
return '$blue-50-opa-20'
|
||||
}
|
||||
|
||||
return '$neutral-95'
|
||||
}
|
||||
|
||||
const renderContent = () => {
|
||||
switch (props.type) {
|
||||
case 'user':
|
||||
case 'account':
|
||||
case 'group':
|
||||
case 'community': {
|
||||
if (!props.src) {
|
||||
return (
|
||||
<Fallback borderRadius={radius} backgroundColor={backgroundColor}>
|
||||
{/* todo?: contrasting color to background */}
|
||||
{props.type === 'group' ? (
|
||||
cloneElement(
|
||||
<MembersIcon
|
||||
size={
|
||||
groupMembersIconSizes[props.size] as IconProps['size']
|
||||
}
|
||||
/>,
|
||||
{
|
||||
color: '$white-100',
|
||||
}
|
||||
)
|
||||
) : (
|
||||
<Text
|
||||
size={textSizes[props.size]}
|
||||
weight="medium"
|
||||
color="$white-100"
|
||||
>
|
||||
{props.name.slice(0, 2).toUpperCase()}
|
||||
</Text>
|
||||
)}
|
||||
</Fallback>
|
||||
)
|
||||
}
|
||||
|
||||
return (
|
||||
<>
|
||||
<Image
|
||||
src={props.src}
|
||||
backgroundColor={backgroundColor}
|
||||
// todo: use tamagui image with token support
|
||||
borderRadius={
|
||||
tokens.radius[
|
||||
radius
|
||||
.toString()
|
||||
.replace('$', '') as keyof typeof tokens.radius
|
||||
].val
|
||||
}
|
||||
width="full"
|
||||
aspectRatio={1}
|
||||
onLoadStart={() => {
|
||||
if (status) {
|
||||
return
|
||||
}
|
||||
|
||||
setStatus('loading')
|
||||
}}
|
||||
onLoad={() => setStatus('loaded')}
|
||||
onError={() => setStatus('error')}
|
||||
/>
|
||||
{/* todo?: add fallback to Image */}
|
||||
{status === 'error' && (
|
||||
<Fallback
|
||||
borderRadius={radius}
|
||||
backgroundColor={backgroundColor}
|
||||
/>
|
||||
)}
|
||||
</>
|
||||
)
|
||||
}
|
||||
case 'wallet':
|
||||
return (
|
||||
<Fallback borderRadius={radius} backgroundColor={backgroundColor}>
|
||||
<Text
|
||||
size={textSizes[props.size]}
|
||||
weight="medium"
|
||||
color="$white-100"
|
||||
>
|
||||
{props.name.slice(0, 2).toUpperCase()}
|
||||
</Text>
|
||||
</Fallback>
|
||||
)
|
||||
case 'channel':
|
||||
return <Text size={channelEmojiSizes[props.size]}>{props.emoji}</Text>
|
||||
case 'icon':
|
||||
return cloneElement(props.icon, { color: props.color ?? '$white-100' })
|
||||
default:
|
||||
return
|
||||
}
|
||||
}
|
||||
|
||||
const renderBadge = () => {
|
||||
switch (props.type) {
|
||||
case 'user': {
|
||||
if (!props.indicator) {
|
||||
return
|
||||
}
|
||||
|
||||
return (
|
||||
<Unspaced>
|
||||
<Indicator size={props.size} state={props.indicator} />
|
||||
</Unspaced>
|
||||
)
|
||||
}
|
||||
case 'channel': {
|
||||
if (!props.lock) {
|
||||
return
|
||||
}
|
||||
|
||||
const iconVariant = channelLockIconVariants[props.size]
|
||||
|
||||
return (
|
||||
<LockBase variant={iconVariant.baseVariant}>
|
||||
{props.lock === 'locked' ? (
|
||||
<LockedIcon size={iconVariant.iconSize as IconProps['size']} />
|
||||
) : (
|
||||
<UnlockedIcon size={iconVariant.iconSize as IconProps['size']} />
|
||||
)}
|
||||
</LockBase>
|
||||
)
|
||||
}
|
||||
default:
|
||||
return
|
||||
}
|
||||
}
|
||||
|
||||
return (
|
||||
<Base size={size} shape={shape} outline={outline}>
|
||||
{indicator !== 'none' && (
|
||||
<Unspaced>
|
||||
<Indicator size={size} state={indicator} />
|
||||
</Unspaced>
|
||||
)}
|
||||
<Shape shape={shape}>
|
||||
<Image
|
||||
src={src}
|
||||
width="full"
|
||||
aspectRatio={1}
|
||||
onLoad={() => setStatus('loaded')}
|
||||
onError={() => setStatus('error')}
|
||||
/>
|
||||
|
||||
{status === 'error' && (
|
||||
<Fallback
|
||||
width={size}
|
||||
height={size}
|
||||
display="flex"
|
||||
alignItems="center"
|
||||
justifyContent="center"
|
||||
>
|
||||
PP
|
||||
</Fallback>
|
||||
)}
|
||||
</Shape>
|
||||
</Base>
|
||||
<Stack style={{ position: 'relative', height: 'fit-content' }}>
|
||||
<Base
|
||||
borderRadius={radius}
|
||||
padding={padding}
|
||||
size={props.size}
|
||||
backgroundColor={backgroundColor}
|
||||
// todo?: https://reactnative.dev/docs/images.html#background-image-via-nesting or svg instead
|
||||
// eslint-disable-next-line @typescript-eslint/ban-ts-comment
|
||||
// @ts-ignore
|
||||
style={{
|
||||
...(Platform.OS === 'web' && {
|
||||
background: identiconRing,
|
||||
}),
|
||||
}}
|
||||
>
|
||||
{renderContent()}
|
||||
</Base>
|
||||
{renderBadge()}
|
||||
</Stack>
|
||||
)
|
||||
}
|
||||
|
||||
export { Avatar }
|
||||
export type { Props as AvatarProps }
|
||||
export type {
|
||||
AccountAvatarProps,
|
||||
AvatarProps,
|
||||
ChannelAvatarProps,
|
||||
CommunityAvatarProps,
|
||||
GroupAvatarProps,
|
||||
IconAvatarProps,
|
||||
UserAvatarProps,
|
||||
WalletAvatarProps,
|
||||
}
|
||||
|
||||
const Base = styled(Stack, {
|
||||
name: 'Avatar',
|
||||
|
@ -74,54 +356,54 @@ const Base = styled(Stack, {
|
|||
position: 'relative',
|
||||
justifyContent: 'center',
|
||||
alignItems: 'center',
|
||||
overflow: 'hidden',
|
||||
|
||||
variants: {
|
||||
// defined in Avatar props
|
||||
size: {
|
||||
'...': (size: number) => {
|
||||
return {
|
||||
width: size,
|
||||
height: size,
|
||||
}
|
||||
80: {
|
||||
width: 80,
|
||||
height: 80,
|
||||
},
|
||||
},
|
||||
|
||||
shape: {
|
||||
circle: {
|
||||
borderRadius: '$full',
|
||||
56: {
|
||||
width: 56,
|
||||
height: 56,
|
||||
},
|
||||
rounded: {
|
||||
borderRadius: '$16',
|
||||
48: {
|
||||
width: 48,
|
||||
height: 48,
|
||||
},
|
||||
},
|
||||
|
||||
outline: {
|
||||
true: {
|
||||
borderWidth: 2,
|
||||
borderColor: '$white-100',
|
||||
32: {
|
||||
width: 32,
|
||||
height: 32,
|
||||
},
|
||||
28: {
|
||||
width: 28,
|
||||
height: 28,
|
||||
},
|
||||
24: {
|
||||
width: 24,
|
||||
height: 24,
|
||||
},
|
||||
20: {
|
||||
width: 20,
|
||||
height: 20,
|
||||
},
|
||||
16: {
|
||||
width: 16,
|
||||
height: 16,
|
||||
},
|
||||
},
|
||||
} as const,
|
||||
})
|
||||
|
||||
const Shape = styled(Stack, {
|
||||
name: 'AvatarShape',
|
||||
const Fallback = styled(Stack, {
|
||||
name: 'AvatarFallback',
|
||||
|
||||
justifyContent: 'center',
|
||||
alignItems: 'center',
|
||||
|
||||
width: '100%',
|
||||
height: '100%',
|
||||
backgroundColor: '$white-100',
|
||||
overflow: 'hidden',
|
||||
|
||||
variants: {
|
||||
shape: {
|
||||
circle: {
|
||||
borderRadius: '$full',
|
||||
},
|
||||
rounded: {
|
||||
borderRadius: '$16',
|
||||
},
|
||||
},
|
||||
},
|
||||
})
|
||||
|
||||
const Indicator = styled(Stack, {
|
||||
|
@ -191,6 +473,31 @@ const Indicator = styled(Stack, {
|
|||
} as const,
|
||||
})
|
||||
|
||||
const Fallback = styled(Text, {
|
||||
name: 'AvatarFallback',
|
||||
const LockBase = styled(Stack, {
|
||||
justifyContent: 'center',
|
||||
alignItems: 'center',
|
||||
width: 16,
|
||||
height: 16,
|
||||
backgroundColor: '$white-100',
|
||||
position: 'absolute',
|
||||
borderRadius: '$full',
|
||||
|
||||
variants: {
|
||||
variant: {
|
||||
80: {
|
||||
width: 48,
|
||||
height: 48,
|
||||
right: -14,
|
||||
bottom: -14,
|
||||
},
|
||||
24: {
|
||||
right: -4,
|
||||
bottom: -4,
|
||||
},
|
||||
20: {
|
||||
right: -6,
|
||||
bottom: -6,
|
||||
},
|
||||
},
|
||||
} as const,
|
||||
})
|
||||
|
|
|
@ -1,85 +0,0 @@
|
|||
import { LockedIcon, UnlockedIcon } from '@status-im/icons'
|
||||
import { type ColorTokens, Stack, styled, Text } from '@tamagui/core'
|
||||
|
||||
type Props = {
|
||||
emoji: string
|
||||
color?: ColorTokens
|
||||
background?: ColorTokens
|
||||
size: 32 | 24 | 20
|
||||
lock?: 'locked' | 'unlocked' | 'none'
|
||||
}
|
||||
|
||||
const emojiSizes: Record<Props['size'], number> = {
|
||||
32: 14,
|
||||
24: 13,
|
||||
20: 11,
|
||||
}
|
||||
|
||||
// https://www.figma.com/file/IBmFKgGL1B4GzqD8LQTw6n/Design-System-for-Desktop%2FWeb?node-id=399-20709&t=kX5LC5OYFnSF8BiZ-11
|
||||
const ChannelAvatar = (props: Props) => {
|
||||
const { emoji, background = '$blue-50-opa-20', size, lock = 'none' } = props
|
||||
|
||||
return (
|
||||
<Base size={size} backgroundColor={background}>
|
||||
{lock !== 'none' && (
|
||||
<LockBase variant={size}>
|
||||
{lock === 'locked' ? (
|
||||
<LockedIcon size={12} />
|
||||
) : (
|
||||
<UnlockedIcon size={12} />
|
||||
)}
|
||||
</LockBase>
|
||||
)}
|
||||
|
||||
<Text fontSize={emojiSizes[size]}>{emoji}</Text>
|
||||
</Base>
|
||||
)
|
||||
}
|
||||
|
||||
export { ChannelAvatar }
|
||||
export type { Props as ChannelAvatarProps }
|
||||
|
||||
const Base = styled(Stack, {
|
||||
position: 'relative',
|
||||
justifyContent: 'center',
|
||||
alignItems: 'center',
|
||||
|
||||
variants: {
|
||||
size: {
|
||||
'...': (size: number) => {
|
||||
return {
|
||||
width: size,
|
||||
height: size,
|
||||
borderRadius: size / 2,
|
||||
}
|
||||
},
|
||||
},
|
||||
},
|
||||
})
|
||||
|
||||
const LockBase = styled(Stack, {
|
||||
justifyContent: 'center',
|
||||
alignItems: 'center',
|
||||
width: 16,
|
||||
height: 16,
|
||||
backgroundColor: '$white-100',
|
||||
position: 'absolute',
|
||||
borderRadius: '$16',
|
||||
|
||||
variants: {
|
||||
variant: {
|
||||
32: {
|
||||
right: -4,
|
||||
bottom: -4,
|
||||
},
|
||||
24: {
|
||||
right: -4,
|
||||
bottom: -4,
|
||||
},
|
||||
20: {
|
||||
right: -6,
|
||||
bottom: -6,
|
||||
},
|
||||
},
|
||||
},
|
||||
})
|
|
@ -1,44 +0,0 @@
|
|||
import { cloneElement } from 'react'
|
||||
|
||||
import { type ColorTokens, Stack, styled } from '@tamagui/core'
|
||||
|
||||
type Props = {
|
||||
children: React.ReactElement
|
||||
backgroundColor?: ColorTokens
|
||||
color?: ColorTokens
|
||||
size?: 20 | 32 | 48
|
||||
}
|
||||
|
||||
const IconAvatar = (props: Props) => {
|
||||
const {
|
||||
children,
|
||||
color = '$blue-50',
|
||||
backgroundColor = '$blue-50-opa-20',
|
||||
size = 32,
|
||||
} = props
|
||||
return (
|
||||
<Base backgroundColor={backgroundColor} size={size}>
|
||||
{cloneElement(children, { color })}
|
||||
</Base>
|
||||
)
|
||||
}
|
||||
|
||||
const Base = styled(Stack, {
|
||||
borderRadius: '$full',
|
||||
justifyContent: 'center',
|
||||
alignItems: 'center',
|
||||
|
||||
variants: {
|
||||
size: {
|
||||
'...': (size: number) => {
|
||||
return {
|
||||
width: size,
|
||||
height: size,
|
||||
}
|
||||
},
|
||||
},
|
||||
},
|
||||
})
|
||||
|
||||
export { IconAvatar }
|
||||
export type { Props as IconAvatarProps }
|
|
@ -1,3 +1 @@
|
|||
export * from './avatar'
|
||||
export * from './channel-avatar'
|
||||
export * from './icon-avatar'
|
||||
|
|
|
@ -0,0 +1,60 @@
|
|||
/**
|
||||
* returns value for conic-gradient
|
||||
*/
|
||||
export const generateIdenticonRing = (colorHash: number[][]) => {
|
||||
const segments = colorHash.reduce((acc, segment) => (acc += segment[0]), 0)
|
||||
|
||||
let prevAngle = 0
|
||||
const gradient = colorHash.reduce((acc, segment, index) => {
|
||||
const [length, colorIndex] = segment
|
||||
const color = COLORS[colorIndex]
|
||||
const nextAngle = Math.round(prevAngle + (length * 360) / segments)
|
||||
|
||||
acc += `${color} ${prevAngle}deg ${nextAngle}deg`
|
||||
|
||||
if (index !== colorHash.length - 1) {
|
||||
acc += `, `
|
||||
}
|
||||
|
||||
prevAngle = nextAngle
|
||||
|
||||
return acc
|
||||
}, '')
|
||||
|
||||
return gradient
|
||||
}
|
||||
|
||||
const COLORS = [
|
||||
'#000000',
|
||||
'#726F6F',
|
||||
'#C4C4C4',
|
||||
'#E7E7E7',
|
||||
'#FFFFFF',
|
||||
'#00FF00',
|
||||
'#009800',
|
||||
'#B8FFBB',
|
||||
'#FFC413',
|
||||
'#9F5947',
|
||||
'#FFFF00',
|
||||
'#A8AC00',
|
||||
'#FFFFB0',
|
||||
'#FF5733',
|
||||
'#FF0000',
|
||||
'#9A0000',
|
||||
'#FF9D9D',
|
||||
'#FF0099',
|
||||
'#C80078',
|
||||
'#FF00FF',
|
||||
'#900090',
|
||||
'#FFB0FF',
|
||||
'#9E00FF',
|
||||
'#0000FF',
|
||||
'#000086',
|
||||
'#9B81FF',
|
||||
'#3FAEF9',
|
||||
'#9A6600',
|
||||
'#00FFFF',
|
||||
'#008694',
|
||||
'#C2FFFF',
|
||||
'#00F0B6',
|
||||
]
|
|
@ -44,9 +44,7 @@ type Story = StoryObj<typeof Channel>
|
|||
|
||||
// More on writing stories with args: https://storybook.js.org/docs/7.0/react/writing-stories/args
|
||||
export const Default: Story = {
|
||||
args: {
|
||||
lock: 'none',
|
||||
},
|
||||
args: {},
|
||||
}
|
||||
|
||||
export const Locked: Story = {
|
||||
|
|
|
@ -3,7 +3,7 @@ import { useState } from 'react'
|
|||
import { MutedIcon, NotificationIcon, OptionsIcon } from '@status-im/icons'
|
||||
import { Stack, styled } from 'tamagui'
|
||||
|
||||
import { ChannelAvatar } from '../avatar'
|
||||
import { Avatar } from '../avatar'
|
||||
import { Counter } from '../counter'
|
||||
import { DropdownMenu } from '../dropdown-menu'
|
||||
import { Text } from '../text'
|
||||
|
@ -95,7 +95,7 @@ const Channel = (props: Props) => {
|
|||
state={active ? 'active' : selected ? 'selected' : undefined}
|
||||
>
|
||||
<Stack flexDirection="row" gap={8} alignItems="center">
|
||||
<ChannelAvatar emoji={emoji} size={24} lock={lock} />
|
||||
<Avatar type="channel" emoji={emoji} size={24} lock={lock} />
|
||||
<Text size={15} weight="medium" color={textColor}>
|
||||
# {children}
|
||||
</Text>
|
||||
|
|
|
@ -63,11 +63,21 @@ const SidebarCommunity = (props: Props) => {
|
|||
>
|
||||
<Stack paddingHorizontal={16} paddingBottom={16}>
|
||||
<Stack marginTop={-40} marginBottom={12}>
|
||||
<Avatar
|
||||
outline
|
||||
src="https://external-content.duckduckgo.com/iu/?u=https%3A%2F%2Fi.seadn.io%2Fgae%2FFG0QJ00fN3c_FWuPeUr9-T__iQl63j9hn5d6svW8UqOmia5zp3lKHPkJuHcvhZ0f_Pd6P2COo9tt9zVUvdPxG_9BBw%3Fw%3D500%26auto%3Dformat&f=1&nofb=1&ipt=c177cd71d8d0114080cfc6efd3f9e098ddaeb1b347919bd3089bf0aacb003b3e&ipo=images"
|
||||
size={80}
|
||||
/>
|
||||
<Stack>
|
||||
<Stack
|
||||
borderWidth={2}
|
||||
borderColor={'$white-100'}
|
||||
borderRadius={'$full'}
|
||||
width="fit-content"
|
||||
>
|
||||
<Avatar
|
||||
type="community"
|
||||
name="Community"
|
||||
src="https://external-content.duckduckgo.com/iu/?u=https%3A%2F%2Fi.seadn.io%2Fgae%2FFG0QJ00fN3c_FWuPeUr9-T__iQl63j9hn5d6svW8UqOmia5zp3lKHPkJuHcvhZ0f_Pd6P2COo9tt9zVUvdPxG_9BBw%3Fw%3D500%26auto%3Dformat&f=1&nofb=1&ipt=c177cd71d8d0114080cfc6efd3f9e098ddaeb1b347919bd3089bf0aacb003b3e&ipo=images"
|
||||
size={80}
|
||||
/>
|
||||
</Stack>
|
||||
</Stack>
|
||||
</Stack>
|
||||
<Stack gap={8} marginBottom={12}>
|
||||
<Text size={27} weight="semibold">
|
||||
|
|
|
@ -65,7 +65,7 @@ const ContextTag = (props: Props) => {
|
|||
|
||||
return (
|
||||
<Base outline={outline} size={size} hasImg={hasImg} blur={blur}>
|
||||
{src && <Avatar size={avatarSizes[size]} src={src} />}
|
||||
{src && <Avatar type="user" name="" size={avatarSizes[size]} src={src} />}
|
||||
{icon && cloneElement(icon, { color: '$neutral-50' })}
|
||||
|
||||
{Array.isArray(label) ? (
|
||||
|
|
|
@ -59,12 +59,26 @@ const Base = styled(RNImage, {
|
|||
variants: {
|
||||
radius: {
|
||||
none: {},
|
||||
6: {
|
||||
borderRadius: 6,
|
||||
},
|
||||
8: {
|
||||
borderRadius: 8,
|
||||
},
|
||||
10: {
|
||||
borderRadius: 10,
|
||||
},
|
||||
12: {
|
||||
borderRadius: 12, // fix this once Image is migrated to tamagui image
|
||||
},
|
||||
16: {
|
||||
borderRadius: 16,
|
||||
},
|
||||
full: {
|
||||
borderRadius: 999, // fix this once Image is migrated to tamagui image
|
||||
},
|
||||
},
|
||||
},
|
||||
|
||||
backgroundColor: '$white-100',
|
||||
})
|
||||
|
|
|
@ -103,6 +103,8 @@ const Message = (props: MessageProps) => {
|
|||
|
||||
<XStack gap={10}>
|
||||
<Avatar
|
||||
type="user"
|
||||
name=""
|
||||
size={32}
|
||||
src="https://images.unsplash.com/photo-1524638431109-93d95c968f03?crop=entropy&cs=tinysrgb&fit=crop&fm=jpg&h=500&ixid=MnwxfDB8MXxyYW5kb218MHx8Z2lybHx8fHx8fDE2NzM4ODQ0NzU&ixlib=rb-4.0.3&q=80&utm_campaign=api-credit&utm_medium=referral&utm_source=unsplash_source&w=500"
|
||||
indicator="online"
|
||||
|
|
|
@ -26,7 +26,7 @@ const Reply = (props: Props) => {
|
|||
</Stack>
|
||||
</Unspaced>
|
||||
|
||||
<Avatar size={16} src={src} />
|
||||
<Avatar type="user" name={name} size={16} src={src} />
|
||||
|
||||
<Text size={13} weight="semibold">
|
||||
{name}
|
||||
|
|
|
@ -1,7 +1,7 @@
|
|||
import { AddUserIcon } from '@status-im/icons'
|
||||
import { Stack } from 'tamagui'
|
||||
|
||||
import { Avatar, IconAvatar } from '../../avatar'
|
||||
import { Avatar } from '../../avatar'
|
||||
import { Text } from '../../text'
|
||||
|
||||
import type { SystemMessageState, User } from '../system-message'
|
||||
|
@ -18,22 +18,29 @@ const AddedUsersMessageContent = (props: Props) => {
|
|||
|
||||
return (
|
||||
<>
|
||||
<IconAvatar
|
||||
<Avatar
|
||||
type="icon"
|
||||
// fixme: map relative avatar size to icon size
|
||||
icon={<AddUserIcon size={20} />}
|
||||
size={32}
|
||||
backgroundColor={state === 'landed' ? '$transparent' : '$blue-50-opa-5'}
|
||||
color="$blue-50"
|
||||
>
|
||||
<AddUserIcon size={20} />
|
||||
</IconAvatar>
|
||||
/>
|
||||
<Stack flexDirection="row" gap={2} flexBasis="max-content" flexGrow={1}>
|
||||
<Stack flexDirection="row" gap={4} alignItems="center" flexGrow={1}>
|
||||
<Avatar size={16} src={user.src} />
|
||||
<Avatar type="user" name={user.name} size={16} src={user.src} />
|
||||
<Text size={13} weight="semibold">
|
||||
{user.name}
|
||||
</Text>
|
||||
<Text size={13}>added </Text>
|
||||
{users.length === 1 && (
|
||||
<Stack flexDirection="row" gap={4} alignItems="center">
|
||||
<Avatar size={16} src={users[0].src} />
|
||||
<Avatar
|
||||
type="user"
|
||||
name={user.name}
|
||||
size={16}
|
||||
src={users[0].src}
|
||||
/>
|
||||
<Text size={13} weight="semibold">
|
||||
{users[0].name}
|
||||
</Text>
|
||||
|
@ -56,7 +63,12 @@ const AddedUsersMessageContent = (props: Props) => {
|
|||
<Text size={13}>
|
||||
{users.length === i + 1 ? ' and ' : null}
|
||||
</Text>
|
||||
<Avatar size={16} src={user.src} />
|
||||
<Avatar
|
||||
type="user"
|
||||
name={user.name}
|
||||
size={16}
|
||||
src={user.src}
|
||||
/>
|
||||
<Text size={13} weight="semibold">
|
||||
{user.name}
|
||||
</Text>
|
||||
|
|
|
@ -1,7 +1,7 @@
|
|||
import { LoadingIcon, TrashIcon } from '@status-im/icons'
|
||||
import { Stack } from 'tamagui'
|
||||
|
||||
import { IconAvatar } from '../../avatar'
|
||||
import { Avatar } from '../../avatar'
|
||||
import { Button } from '../../button'
|
||||
import { Text } from '../../text'
|
||||
|
||||
|
@ -22,12 +22,13 @@ const DeletedMessageContent = (props: Props) => {
|
|||
|
||||
return (
|
||||
<>
|
||||
<IconAvatar
|
||||
<Avatar
|
||||
type="icon"
|
||||
size={32}
|
||||
icon={<TrashIcon size={20} />}
|
||||
backgroundColor={state === 'landed' ? '$transparent' : '$red-50-opa-5'}
|
||||
color="$neutral-100"
|
||||
>
|
||||
<TrashIcon size={20} />
|
||||
</IconAvatar>
|
||||
/>
|
||||
<Stack
|
||||
flexDirection="row"
|
||||
gap={2}
|
||||
|
|
|
@ -1,7 +1,7 @@
|
|||
import { PinIcon } from '@status-im/icons'
|
||||
import { Stack } from 'tamagui'
|
||||
|
||||
import { Avatar, IconAvatar } from '../../avatar'
|
||||
import { Avatar } from '../../avatar'
|
||||
import { Text } from '../../text'
|
||||
|
||||
import type { SystemMessageState, User } from '../system-message'
|
||||
|
@ -27,12 +27,13 @@ const PinnedMessageContent = (props: Props) => {
|
|||
|
||||
return (
|
||||
<>
|
||||
<IconAvatar
|
||||
<Avatar
|
||||
type="icon"
|
||||
size={32}
|
||||
icon={<PinIcon size={20} />}
|
||||
backgroundColor={state === 'landed' ? '$transparent' : '$blue-50-opa-5'}
|
||||
color="$neutral-100"
|
||||
>
|
||||
<PinIcon size={20} />
|
||||
</IconAvatar>
|
||||
/>
|
||||
<Stack
|
||||
flexDirection="column"
|
||||
gap={2}
|
||||
|
@ -56,7 +57,7 @@ const PinnedMessageContent = (props: Props) => {
|
|||
flexBasis="max-content"
|
||||
>
|
||||
<Stack flexDirection="row" gap={4}>
|
||||
<Avatar size={16} src={author.src} />
|
||||
<Avatar type="user" name={author.name} size={16} src={author.src} />
|
||||
<Text size={11} weight="semibold">
|
||||
{author.name}
|
||||
</Text>
|
||||
|
|
|
@ -12,6 +12,7 @@ type Weight = NonNullable<Variants['weight']>
|
|||
|
||||
type Props = {
|
||||
children: React.ReactNode
|
||||
size?: 27 | 19 | 15 | 13 | 11 | undefined
|
||||
color?: ColorTokens
|
||||
truncate?: boolean
|
||||
wrap?: false
|
||||
|
@ -26,8 +27,8 @@ type Props = {
|
|||
// TODO: monospace should be used only for variant. Extract to separate <Address> component?
|
||||
// TODO: Ubuntu Mono should be used only for code snippets. Extract to separate <Code> component?
|
||||
const Text = (props: Props, ref: Ref<RNText>) => {
|
||||
const { color = '$neutral-100', ...rest } = props
|
||||
return <Base {...rest} ref={ref} color={color} />
|
||||
const { color = '$neutral-100', size = 13, ...rest } = props
|
||||
return <Base {...rest} ref={ref} color={color} size={size} />
|
||||
}
|
||||
|
||||
const Base = styled(BaseText, {
|
||||
|
|
|
@ -5,10 +5,10 @@ import { Avatar } from '../avatar'
|
|||
import { Text } from '../text'
|
||||
|
||||
import type { AuthorProps } from '../author/author'
|
||||
import type { AvatarProps } from '../avatar'
|
||||
import type { UserAvatarProps } from '../avatar'
|
||||
|
||||
type Props = {
|
||||
users: (Pick<AvatarProps, 'src' | 'indicator'> & AuthorProps)[]
|
||||
users: (Pick<UserAvatarProps, 'src' | 'indicator'> & AuthorProps)[]
|
||||
}
|
||||
|
||||
const UserList = (props: Props) => {
|
||||
|
@ -31,7 +31,13 @@ const UserList = (props: Props) => {
|
|||
backgroundColor: '$primary-50-opa-5',
|
||||
}}
|
||||
>
|
||||
<Avatar size={32} src={src} indicator={indicator} />
|
||||
<Avatar
|
||||
type="user"
|
||||
name={authorProps.name}
|
||||
size={32}
|
||||
src={src}
|
||||
indicator={indicator}
|
||||
/>
|
||||
<YStack>
|
||||
<Author {...authorProps} />
|
||||
<Text size={13} color="$neutral-50" type="monospace">
|
||||
|
|
Loading…
Reference in New Issue