Skip to content

Commit 90393f4

Browse files
committed
feat(AvatarGroup): add overflow slot for custom tooltip on hidden avatars
1 parent 92cfda0 commit 90393f4

4 files changed

Lines changed: 73 additions & 1 deletion

File tree

Lines changed: 31 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,31 @@
1+
<script setup lang="ts">
2+
const getOverflowTooltip = (hiddenCount: number) => `${hiddenCount} more teammates`
3+
</script>
4+
5+
<template>
6+
<UAvatarGroup :max="2">
7+
<UAvatar
8+
src="https://github.com/benjamincanac.png"
9+
alt="Benjamin Canac"
10+
loading="lazy"
11+
/>
12+
13+
<UAvatar
14+
src="https://github.com/romhml.png"
15+
alt="Romain Hamel"
16+
loading="lazy"
17+
/>
18+
19+
<UAvatar
20+
src="https://github.com/noook.png"
21+
alt="Neil Richter"
22+
loading="lazy"
23+
/>
24+
25+
<template #overflow="{ hiddenCount, avatarProps }">
26+
<UTooltip :text="getOverflowTooltip(hiddenCount)">
27+
<UAvatar v-bind="avatarProps" />
28+
</UTooltip>
29+
</template>
30+
</UAvatarGroup>
31+
</template>

docs/content/docs/2.components/avatar-group.md

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -69,6 +69,12 @@ slots:
6969
:u-avatar{src="https://github.com/noook.png" alt="Neil Richter" loading="lazy"}
7070
::
7171

72+
### Overflow tooltip
73+
74+
Use the `overflow` slot to customize the `+X` avatar. The slot receives the hidden count and the default avatar props, so you can wrap the overflow avatar with a [Tooltip](/docs/components/tooltip) and provide either a static string or a helper function that derives the label from the count.
75+
76+
:component-example{name="avatar-group-overflow-tooltip-example"}
77+
7278
## Examples
7379

7480
### With tooltip

src/runtime/components/AvatarGroup.vue

Lines changed: 9 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -26,6 +26,7 @@ export interface AvatarGroupProps {
2626
2727
export interface AvatarGroupSlots {
2828
default?(props?: {}): VNode[]
29+
overflow?(props: { hiddenCount: number, avatarProps: { text: string, class: any } }): VNode[]
2930
}
3031
</script>
3132

@@ -90,14 +91,21 @@ const hiddenCount = computed(() => {
9091
return children.value.length - visibleAvatars.value.length
9192
})
9293
94+
const overflowAvatarProps = computed(() => ({
95+
text: `+${hiddenCount.value}`,
96+
class: ui.value.base({ class: uiProp.value?.base })
97+
}))
98+
9399
provide(avatarGroupInjectionKey, computed(() => ({
94100
size: props.size
95101
})))
96102
</script>
97103

98104
<template>
99105
<Primitive :as="as" data-slot="root" :class="ui.root({ class: [uiProp?.root, props.class] })">
100-
<UAvatar v-if="hiddenCount > 0" :text="`+${hiddenCount}`" data-slot="base" :class="ui.base({ class: uiProp?.base })" />
106+
<slot v-if="hiddenCount > 0" name="overflow" :hidden-count="hiddenCount" :avatar-props="overflowAvatarProps">
107+
<UAvatar v-bind="overflowAvatarProps" data-slot="base" />
108+
</slot>
101109
<component :is="avatar" v-for="(avatar, count) in visibleAvatars" :key="count" data-slot="base" :class="ui.base({ class: uiProp?.base })" />
102110
</Primitive>
103111
</template>

test/components/AvatarGroup.spec.ts

Lines changed: 27 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,7 @@ import { mountSuspended } from '@nuxt/test-utils/runtime'
55
import { renderEach } from '../component-render'
66
import AvatarGroup from '../../src/runtime/components/AvatarGroup.vue'
77
import Avatar from '../../src/runtime/components/Avatar.vue'
8+
import Tooltip from '../../src/runtime/components/Tooltip.vue'
89
import theme from '#build/ui/avatar-group'
910

1011
const AvatarGroupWrapper = defineComponent({
@@ -19,6 +20,26 @@ const AvatarGroupWrapper = defineComponent({
1920
</UAvatarGroup>`
2021
})
2122

23+
const AvatarGroupOverflowTooltipWrapper = defineComponent({
24+
components: {
25+
UAvatar: Avatar,
26+
UAvatarGroup: AvatarGroup,
27+
UTooltip: Tooltip
28+
},
29+
template: `<UAvatarGroup :max="2">
30+
<UAvatar src="https://github.com/benjamincanac.png" alt="Benjamin Canac" />
31+
<UAvatar src="https://github.com/romhml.png" alt="Romain Hamel" />
32+
<UAvatar src="https://github.com/noook.png" alt="Neil Richter" />
33+
<template #overflow="{ hiddenCount, avatarProps }">
34+
<span :data-hidden-count="hiddenCount">
35+
<UTooltip :text="'+' + hiddenCount + ' hidden avatars'">
36+
<UAvatar v-bind="avatarProps" />
37+
</UTooltip>
38+
</span>
39+
</template>
40+
</UAvatarGroup>`
41+
})
42+
2243
describe('AvatarGroup', () => {
2344
const sizes = Object.keys(theme.variants.size) as any
2445

@@ -33,6 +54,12 @@ describe('AvatarGroup', () => {
3354
['with default slot', {}]
3455
])
3556

57+
it('exposes the hidden count to the overflow slot', async () => {
58+
const wrapper = await mountSuspended(AvatarGroupOverflowTooltipWrapper)
59+
60+
expect(wrapper.find('[data-hidden-count="1"]').exists()).toBe(true)
61+
})
62+
3663
it('passes accessibility tests', async () => {
3764
const wrapper = await mountSuspended(AvatarGroupWrapper, {
3865
props: {

0 commit comments

Comments
 (0)