Files
BRETT/app/components/UserHoverCard.vue
T
2026-10-03 20:07:49 +02:00

99 lines
3.9 KiB
Vue

<script setup lang="ts">
import {
HoverCard,
HoverCardContent,
HoverCardTrigger,
} from '@/components/ui/hover-card'
import type { CardBorderEffectKey } from '~~/shared/utils/cardBorderEffects'
import { isCardBorderEffect } from '~~/shared/utils/cardBorderEffects'
import type { CardEffectKey } from '~~/shared/utils/cardEffects'
import { isCardEffect } from '~~/shared/utils/cardEffects'
import CardBackgroundEffect from '@/components/effects/CardBackgroundEffect.vue'
import CardBorderEffect from '@/components/effects/CardBorderEffect.vue'
import CardVignette from '@/components/effects/CardVignette.vue'
const props = defineProps<{
userId: number
userName: string
userColor?: string | null
userAvatarUrl?: string | null
}>()
const { summaries, ensureSummary } = useUserSummaries()
const summary = computed(() => summaries.value[props.userId])
const avatar = computed(() => summary.value?.avatarUrl ?? props.userAvatarUrl ?? null)
const color = computed(() => summary.value?.color ?? props.userColor ?? null)
const effect = computed<CardEffectKey | null>(() => {
const key = summary.value?.cardEffect
return key && isCardEffect(key) ? key : null
})
const borderEffect = computed<CardBorderEffectKey | null>(() => {
const key = summary.value?.cardBorderEffect
return key && isCardBorderEffect(key) ? key : null
})
function onOpen(open: boolean) {
if (open) ensureSummary(props.userId)
}
function formatDate(value: string) {
return new Date(value).toLocaleDateString('de-DE', { day: '2-digit', month: 'short', year: 'numeric' })
}
</script>
<template>
<HoverCard :open-delay="200" :close-delay="150" @update:open="onOpen">
<HoverCardTrigger as-child>
<slot />
</HoverCardTrigger>
<HoverCardContent class="relative w-72 overflow-hidden rounded-md">
<div v-if="effect" class="pointer-events-none absolute inset-0 overflow-hidden rounded-[inherit] [clip-path:inset(0_round_var(--radius-md))]">
<CardBackgroundEffect :effect="effect" :color="summary?.cardEffectColor ?? color ?? '#6366f1'" />
<div class="absolute inset-0 bg-popover/60" />
<CardVignette v-if="summary?.cardVignette" />
</div>
<div class="relative flex items-start gap-3">
<NuxtImg
v-if="avatar"
:src="avatar"
:alt="userName"
width="56"
height="56"
fit="cover"
class="size-14 shrink-0 rounded-md"
/>
<span
v-else
class="flex size-14 shrink-0 items-center justify-center rounded-md text-lg font-medium"
:style="color ? { backgroundColor: color, color: '#fff' } : { backgroundColor: 'var(--color-muted)' }"
>{{ userName.slice(0, 2).toUpperCase() }}</span>
<div class="min-w-0">
<NuxtLink
:to="`/user/${userId}`"
class="block truncate text-base font-medium hover:underline"
:style="color ? { color } : undefined"
>{{ summary?.name ?? userName }}</NuxtLink>
<p class="text-xs text-muted-foreground">
#{{ userId }}
</p>
<p v-if="summary" class="mt-1 flex items-center gap-1 text-xs text-muted-foreground">
<Icon name="ph:calendar-fill" class="size-3" />
seit {{ formatDate(summary.createdAt) }}
</p>
</div>
</div>
<div v-if="summary" class="relative mt-3 flex items-center gap-4 border-t pt-3 text-xs text-muted-foreground">
<span class="flex items-center gap-1.5">
<Icon name="ph:cloud-arrow-up-fill" class="size-3.5" />
{{ summary.stats.uploads }} Uploads
</span>
<span class="flex items-center gap-1.5">
<Icon name="ph:chat-fill" class="size-3.5" />
{{ summary.stats.comments }} Kommentare
</span>
</div>
<CardBorderEffect v-if="borderEffect" :effect="borderEffect" :color="summary?.cardBorderEffectColor ?? color ?? '#6366f1'" />
</HoverCardContent>
</HoverCard>
</template>