@@ -12,7 +12,7 @@ import { LinkBox, LinkOverlay } from "./link-box"
12
12
const avatarStyles = tv ( {
13
13
slots : {
14
14
container :
15
- "relative shrink-0 flex overflow-hidden rounded-full focus:outline-4 focus:-outline-offset-1 focus:rounded-full active:shadow-none [&_img]:hover:opacity-70 border border-transparent active:border-primary-hover" ,
15
+ "relative shrink-0 flex overflow-hidden rounded-full focus:outline-4 focus:-outline-offset-1 focus:rounded-full active:shadow-none [&_img]:hover:opacity-70 border border-transparent active:border-primary-hover justify-center items-center " ,
16
16
fallback : "bg-body text-body-inverse" ,
17
17
} ,
18
18
variants : {
@@ -115,13 +115,23 @@ export type AvatarProps = AvatarBaseProps &
115
115
direction ?: "column" | "row"
116
116
name : string
117
117
src : string
118
+ dataTest ?: string
118
119
}
119
120
120
121
const Avatar = React . forwardRef <
121
122
React . ElementRef < "span" > | React . ElementRef < "div" > ,
122
123
AvatarProps
123
124
> ( ( props , ref ) => {
124
- const { href, src, name, size, label, direction = "row" } = props
125
+ const {
126
+ href,
127
+ src,
128
+ name,
129
+ size,
130
+ label,
131
+ className,
132
+ direction = "row" ,
133
+ dataTest,
134
+ } = props
125
135
126
136
const commonLinkProps = {
127
137
href,
@@ -165,8 +175,8 @@ const Avatar = React.forwardRef<
165
175
}
166
176
167
177
return (
168
- < AvatarBase ref = { ref } size = { size } asChild >
169
- < BaseLink { ...commonLinkProps } >
178
+ < AvatarBase ref = { ref } size = { size } className = { className } asChild >
179
+ < BaseLink title = { dataTest } { ...commonLinkProps } >
170
180
< AvatarImage src = { src } />
171
181
< AvatarFallback > { fallbackInitials } </ AvatarFallback >
172
182
</ BaseLink >
@@ -175,4 +185,65 @@ const Avatar = React.forwardRef<
175
185
} )
176
186
Avatar . displayName = "Avatar"
177
187
178
- export { Avatar , AvatarBase , AvatarFallback , AvatarImage }
188
+ type AvatarGroupProps = AvatarVariantProps &
189
+ React . HTMLAttributes < HTMLDivElement > & {
190
+ children : React . ReactNode
191
+ max ?: number
192
+ }
193
+ /**
194
+ * Chakra v2 component as reference: https://github.com/chakra-ui/chakra-ui/blob/v2/packages/components/src/avatar/avatar-group.tsx
195
+ */
196
+ const AvatarGroup = React . forwardRef < HTMLDivElement , AvatarGroupProps > (
197
+ ( props , ref ) => {
198
+ const { children, max, size, className, ...rest } = props
199
+
200
+ const validChildren = React . Children . toArray ( children ) . filter ( ( child ) =>
201
+ React . isValidElement ( child )
202
+ ) as React . ReactElement [ ]
203
+
204
+ /**
205
+ * The visible avatars from max
206
+ */
207
+ const childrenWithinMax =
208
+ max != null ? validChildren . slice ( 0 , max ) : validChildren
209
+ /**
210
+ * Number of hidden avatars from max
211
+ */
212
+ const hiddenCount = max != null ? validChildren . length - max : 0
213
+
214
+ /**
215
+ * Reversed children to handle implied z-index
216
+ */
217
+ const reversedChildren = childrenWithinMax . reverse ( )
218
+
219
+ const clonedChildren = reversedChildren . map ( ( child , idx ) => {
220
+ const isFirst = idx === 0
221
+ return React . cloneElement ( child , {
222
+ className : cn ( isFirst ? "me-0" : "-me-2" ) ,
223
+ size,
224
+ } )
225
+ } )
226
+
227
+ const { container, fallback } = avatarStyles ( { size } )
228
+
229
+ return (
230
+ < div
231
+ ref = { ref }
232
+ role = "group"
233
+ className = { cn ( "flex flex-row-reverse" , className ) }
234
+ { ...rest }
235
+ >
236
+ { hiddenCount > 0 && (
237
+ < span
238
+ className = { cn ( "-ms-2" , container ( ) , fallback ( ) ) }
239
+ > { `+${ hiddenCount } ` } </ span >
240
+ ) }
241
+ { clonedChildren }
242
+ </ div >
243
+ )
244
+ }
245
+ )
246
+
247
+ AvatarGroup . displayName = "AvatarGroup"
248
+
249
+ export { Avatar , AvatarBase , AvatarFallback , AvatarGroup , AvatarImage }
0 commit comments