1<script setup lang="ts">
2import { computed, onBeforeUnmount } from 'vue'
3import { usePresenceChannel } from '@/composables/usePresenceChannel'
4import type { WorkspaceMember } from '@/types/workspace'
5
6const props = defineProps<{
7 workspaceId: string
8 members: WorkspaceMember[]
9 maxVisible?: number
10}>()
11
12const emit = defineEmits<{
13 select: [member: WorkspaceMember]
14}>()
15
16const maxVisible = computed(() => props.maxVisible ?? 4)
17const { onlineIds, disconnect } = usePresenceChannel(props.workspaceId)
18
19const onlineMembers = computed(() =>
20 props.members
21 .filter((member) => onlineIds.value.has(member.id))
22 .sort((a, b) => a.name.localeCompare(b.name))
23)
24
25+const visibleMembers = computed(() =>
26+ onlineMembers.value.slice(0, maxVisible.value)
27+)
28
29+const overflowCount = computed(() =>
30+ Math.max(0, onlineMembers.value.length - visibleMembers.value.length)
31+)
32
33onBeforeUnmount(disconnect)
34</script>
35
36<template>
37 <TransitionGroup name="presence" tag="div" class="presence-stack">
38+ <button v-for="member in visibleMembers" :key="member.id"
39+ class="avatar" @click="emit('select', member)">
40+ <img :src="member.avatarUrl" :alt="member.name">
41+ <span class="online-dot" />
42+ </button>
43+ <span v-if="overflowCount" class="overflow">+{{ overflowCount }}</span>
44 </TransitionGroup>
45</template>