Skip to content

Commit 002e4c2

Browse files
chore: more design change updates (#2953)
### 🎯 Goal - [x] adjust Avatar sizing (fix 1/1 ratio) - [x] add border to the `AvatarStack` - [x] add "overflow" badge to the `AvatarStack` - [x] fix `ReactionsList` positioning above message bubble - [x] add "overflow" badge to the GroupAvatar component - [ ] add "overflow" badge to the ReactionsList - [x] break ReactionsList (bottom variant) if it grows beyond message max-width - [x] add 2xl avatar variants (Avatar, GroupAvatar)
1 parent ba65752 commit 002e4c2

File tree

9 files changed

+214
-67
lines changed

9 files changed

+214
-67
lines changed

src/components/Avatar/Avatar.tsx

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -15,7 +15,7 @@ export type AvatarProps = {
1515
/** Online status indicator, not rendered if not of type boolean */
1616
isOnline?: boolean;
1717

18-
size: 'xl' | 'lg' | 'md' | 'sm' | 'xs' | null;
18+
size: '2xl' | 'xl' | 'lg' | 'md' | 'sm' | 'xs' | null;
1919
} & ComponentPropsWithoutRef<'div'>;
2020

2121
const getInitials = (name?: string) => {

src/components/Avatar/AvatarStack.tsx

Lines changed: 14 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,13 +1,18 @@
11
import { type ComponentProps, type ElementType } from 'react';
22
import { useComponentContext } from '../../context';
33
import { type AvatarProps, Avatar as DefaultAvatar } from './Avatar';
4+
import clsx from 'clsx';
45

56
export function AvatarStack({
67
component: Component = 'div',
78
displayInfo = [],
9+
overflowCount,
10+
size,
811
}: {
912
component?: ElementType;
1013
displayInfo?: (Pick<AvatarProps, 'imageUrl' | 'userName'> & { id?: string })[];
14+
overflowCount?: number;
15+
size: 'sm' | 'xs' | null;
1116
}) {
1217
const { Avatar = DefaultAvatar } = useComponentContext(AvatarStack.name);
1318

@@ -16,16 +21,22 @@ export function AvatarStack({
1621
}
1722

1823
return (
19-
<Component className='str-chat__avatar-stack'>
24+
<Component
25+
className={clsx('str-chat__avatar-stack', {
26+
[`str-chat__avatar-stack--size-${size}`]: typeof size === 'string',
27+
})}
28+
>
2029
{displayInfo.map((info, index) => (
2130
<Avatar
22-
className='str-chat__avatar--with-border'
2331
imageUrl={info.imageUrl}
2432
key={info.id ?? `${info.userName}-${info.imageUrl}-${index}`}
25-
size='xs'
33+
size={size}
2634
userName={info.userName}
2735
/>
2836
))}
37+
{typeof overflowCount === 'number' && overflowCount > 0 && (
38+
<div className='str-chat__avatar-stack__count-badge'>{overflowCount}</div>
39+
)}
2940
</Component>
3041
);
3142
}

src/components/Avatar/GroupAvatar.tsx

Lines changed: 21 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -6,8 +6,9 @@ import type { GroupChannelDisplayInfo } from '../ChannelPreview';
66
export type GroupAvatarProps = ComponentPropsWithoutRef<'div'> & {
77
/** Mapping of image URLs to names which initials will be used as fallbacks in case image assets fail to load. */
88
groupChannelDisplayInfo: GroupChannelDisplayInfo;
9-
size: 'xl' | 'lg' | null;
9+
size: '2xl' | 'xl' | 'lg' | null;
1010
isOnline?: boolean;
11+
overflowCount?: number;
1112
};
1213

1314
/**
@@ -19,9 +20,12 @@ export const GroupAvatar = ({
1920
className,
2021
groupChannelDisplayInfo,
2122
isOnline,
23+
overflowCount,
2224
size,
2325
...rest
2426
}: GroupAvatarProps) => {
27+
const displayCountBadge = typeof overflowCount === 'number' && overflowCount > 0;
28+
2529
if (!groupChannelDisplayInfo || groupChannelDisplayInfo.length < 2) {
2630
const [firstUser] = groupChannelDisplayInfo || [];
2731

@@ -37,9 +41,10 @@ export const GroupAvatar = ({
3741
}
3842

3943
let avatarSize: AvatarProps['size'] = null;
40-
41-
if (size === 'xl') {
44+
if (size === '2xl') {
4245
avatarSize = 'lg';
46+
} else if (size === 'xl') {
47+
avatarSize = 'md';
4348
} else if (size === 'lg') {
4449
avatarSize = 'sm';
4550
}
@@ -59,14 +64,19 @@ export const GroupAvatar = ({
5964
role='button'
6065
{...rest}
6166
>
62-
{groupChannelDisplayInfo.slice(0, 4).map(({ imageUrl, userName }, index) => (
63-
<Avatar
64-
imageUrl={imageUrl}
65-
key={`${userName}-${imageUrl}-${index}`}
66-
size={avatarSize}
67-
userName={userName}
68-
/>
69-
))}
67+
{groupChannelDisplayInfo
68+
.slice(0, displayCountBadge ? 2 : 4)
69+
.map(({ imageUrl, userName }, index) => (
70+
<Avatar
71+
imageUrl={imageUrl}
72+
key={`${userName}-${imageUrl}-${index}`}
73+
size={avatarSize}
74+
userName={userName}
75+
/>
76+
))}
77+
{displayCountBadge && (
78+
<div className='str-chat__avatar-group__count-badge'>+{overflowCount}</div>
79+
)}
7080
</div>
7181
);
7282
};

src/components/Avatar/styling/Avatar.scss

Lines changed: 37 additions & 27 deletions
Original file line numberDiff line numberDiff line change
@@ -4,17 +4,18 @@
44
display: flex;
55
justify-content: center;
66
align-items: center;
7-
border-radius: var(--radius-max, 9999px);
8-
background: var(--avatar-bg-default, #e3edff);
9-
color: var(--avatar-text-default, #142f63);
7+
border-radius: var(--radius-max);
8+
background: var(--avatar-bg-default);
9+
color: var(--avatar-text-default);
1010
text-align: center;
1111

1212
font-weight: var(--typography-font-weight-semi-bold);
13+
font-style: normal;
1314
line-height: 1;
1415
text-transform: uppercase;
1516
width: var(--avatar-size);
16-
aspect-ratio: 1/1;
17-
--avatar-badge-angle: -45deg;
17+
height: var(--avatar-size);
18+
--avatar-online-badge-angle: -45deg;
1819

1920
// FIXME: temporary thing, should be removed when we get rid of the old CSS
2021
grid-area: avatar;
@@ -47,65 +48,74 @@
4748
aspect-ratio: 1/1;
4849
content: '';
4950
position: absolute;
50-
width: var(--avatar-badge-size);
51+
width: var(--avatar-online-badge-size);
5152

5253
left: calc(
5354
var(--avatar-size) / 2 + var(--avatar-size) / 2 *
54-
cos(var(--avatar-badge-angle)) - var(--avatar-badge-size) / 2
55+
cos(var(--avatar-online-badge-angle)) - var(--avatar-online-badge-size) / 2
5556
);
5657
top: calc(
5758
var(--avatar-size) / 2 + var(--avatar-size) / 2 *
58-
sin(var(--avatar-badge-angle)) - var(--avatar-badge-size) / 2
59+
sin(var(--avatar-online-badge-angle)) - var(--avatar-online-badge-size) / 2
5960
);
6061

6162
border-radius: var(--radius-max, 9999px);
6263
border-style: solid;
63-
border-color: var(--presence-border, #fff);
64+
border-color: var(--presence-border);
6465
border-width: 2px;
6566
}
6667

6768
&.str-chat__avatar--online::after {
68-
background: var(--presence-bg-online, #00c384);
69+
background: var(--presence-bg-online);
6970
}
7071

7172
&.str-chat__avatar--offline::after {
72-
background: var(--presence-bg-offline, #687385);
73+
background: var(--presence-bg-offline);
7374
}
7475

75-
&.str-chat__avatar--size-xl {
76+
&.str-chat__avatar--size-2xl {
7677
--avatar-size: 64px;
77-
--avatar-badge-size: 16px;
78-
--avatar-icon-size: 32px;
78+
--avatar-online-badge-size: 16px;
79+
--avatar-icon-size: var(--icon-size-lg);
80+
--avatar-icon-stroke-width: 1.5px;
81+
82+
font-size: var(--typography-font-size-xl);
83+
}
84+
85+
&.str-chat__avatar--size-xl {
86+
--avatar-size: 48px;
87+
--avatar-online-badge-size: 16px;
88+
--avatar-icon-size: var(--size-24); // TODO: missing icon size
7989
--avatar-icon-stroke-width: 1.5px;
8090

81-
font-size: var(--typography-font-size-xl, 20px);
91+
font-size: var(--typography-font-size-xl);
8292
}
8393

8494
&.str-chat__avatar--size-lg {
8595
--avatar-size: 40px;
86-
--avatar-badge-size: 14px;
87-
--avatar-icon-size: 20px;
96+
--avatar-online-badge-size: 14px;
97+
--avatar-icon-size: var(--icon-size-md);
8898
--avatar-icon-stroke-width: 1.5px;
8999

90-
font-size: var(--typography-font-size-md, 15px);
100+
font-size: var(--typography-font-size-md);
91101
}
92102

93-
&.str-chat__avatar--size-md {
103+
&.str-chat__avatar--size-md {
94104
--avatar-size: 32px;
95-
--avatar-badge-size: 12px;
96-
--avatar-icon-size: 16px;
105+
--avatar-online-badge-size: 12px;
106+
--avatar-icon-size: var(--icon-size-md);
97107
--avatar-icon-stroke-width: 1.5px;
98108

99-
font-size: var(--typography-font-size-sm, 13px);
109+
font-size: var(--typography-font-size-sm);
100110
}
101111

102112
&.str-chat__avatar--size-sm {
103113
--avatar-size: 24px;
104-
--avatar-badge-size: 8px;
105-
--avatar-icon-size: 12px;
114+
--avatar-online-badge-size: 8px;
115+
--avatar-icon-size: var(--icon-size-sm);
106116
--avatar-icon-stroke-width: 1.2px;
107117

108-
font-size: var(--typography-font-size-sm, 13px);
118+
font-size: var(--typography-font-size-sm);
109119

110120
&.str-chat__avatar--offline::after,
111121
&.str-chat__avatar--online::after {
@@ -115,11 +125,11 @@
115125

116126
&.str-chat__avatar--size-xs {
117127
--avatar-size: 20px;
118-
--avatar-badge-size: 8px;
128+
--avatar-online-badge-size: 8px;
119129
--avatar-icon-size: 10px;
120130
--avatar-icon-stroke-width: 1.2px;
121131

122-
font-size: var(--typography-font-size-xs, 12px);
132+
font-size: var(--typography-font-size-xs);
123133

124134
&.str-chat__avatar--offline::after,
125135
&.str-chat__avatar--online::after {

src/components/Avatar/styling/AvatarStack.scss

Lines changed: 47 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,53 @@
22
display: flex;
33
align-items: center;
44

5-
& > .str-chat__avatar:not(:first-child) {
5+
& > .str-chat__avatar:not(:first-child),
6+
.str-chat__avatar-stack__count-badge {
67
margin-left: calc(var(--spacing-xs) * -1);
78
}
9+
10+
& > .str-chat__avatar:after {
11+
content: '';
12+
position: absolute;
13+
width: calc(100% + 4px);
14+
height: calc(100% + 4px);
15+
border: 2px solid var(--border-core-on-dark);
16+
border-radius: inherit;
17+
}
18+
19+
&.str-chat__avatar-stack--size-sm {
20+
--avatar-stack-count-badge-size: 24px;
21+
// FIXME?: should be sm but it looks way too big
22+
--avatar-stack-count-badge-font-size: var(--typography-font-size-xs);
23+
24+
.str-chat__avatar-stack__count-badge {
25+
padding-inline: var(--spacing-xs);
26+
}
27+
}
28+
29+
&.str-chat__avatar-stack--size-xs {
30+
--avatar-stack-count-badge-size: 20px;
31+
--avatar-stack-count-badge-font-size: var(--typography-font-size-xxs);
32+
33+
.str-chat__avatar-stack__count-badge {
34+
padding-inline: var(--spacing-xxs);
35+
}
36+
}
37+
38+
.str-chat__avatar-stack__count-badge {
39+
font-size: var(--avatar-stack-count-badge-font-size);
40+
font-weight: var(--typography-font-weight-bold);
41+
display: flex;
42+
justify-content: center;
43+
align-items: center;
44+
height: var(--avatar-stack-count-badge-size);
45+
min-width: var(--avatar-stack-count-badge-size);
46+
min-height: var(--avatar-stack-count-badge-size);
47+
border-radius: var(--radius-max);
48+
border: 1px solid var(--border-core-subtle);
49+
background: var(--badge-bg-default);
50+
box-shadow: 0 2px 6px 0 rgba(0, 0, 0, 0.14);
51+
line-height: 1;
52+
z-index: 1;
53+
}
854
}

0 commit comments

Comments
 (0)