83 lines
1.8 KiB
Vue
83 lines
1.8 KiB
Vue
<script setup lang="ts">
|
|
import { computed, ref, watch } from 'vue';
|
|
import { personInitials } from '@/lib/person-insights';
|
|
import { DEFAULT_USER_AVATAR, primeUserAvatar, userAvatarSrc } from '@/lib/userAvatars';
|
|
|
|
const props = withDefaults(
|
|
defineProps<{
|
|
name?: string | null;
|
|
src?: string | null;
|
|
/** شناسه کاربر سیستم؛ اگر داده شود عکس پروفایل واقعی او نمایش داده میشود */
|
|
userId?: string | null;
|
|
hasAvatar?: boolean;
|
|
size?: number;
|
|
}>(),
|
|
{
|
|
name: '',
|
|
src: undefined,
|
|
userId: null,
|
|
hasAvatar: undefined,
|
|
size: 28,
|
|
},
|
|
);
|
|
|
|
watch(
|
|
() => [props.userId, props.hasAvatar] as const,
|
|
([id, has]) => {
|
|
if (id) primeUserAvatar(id, has);
|
|
},
|
|
{ immediate: true },
|
|
);
|
|
|
|
const resolvedSrc = computed(() => {
|
|
if (props.src !== undefined) return props.src;
|
|
if (props.userId) return userAvatarSrc(props.userId);
|
|
return DEFAULT_USER_AVATAR;
|
|
});
|
|
|
|
const initials = computed(() => personInitials(props.name));
|
|
const failed = ref(false);
|
|
watch(resolvedSrc, () => {
|
|
failed.value = false;
|
|
});
|
|
</script>
|
|
|
|
<template>
|
|
<span class="user-avatar" :style="{ width: `${size}px`, height: `${size}px` }" :title="name || undefined">
|
|
<img
|
|
v-if="resolvedSrc && !failed"
|
|
:src="resolvedSrc"
|
|
:alt="name || 'آواتار'"
|
|
@error="failed = true"
|
|
/>
|
|
<em v-else>{{ initials }}</em>
|
|
</span>
|
|
</template>
|
|
|
|
<style scoped>
|
|
.user-avatar {
|
|
display: inline-grid;
|
|
place-items: center;
|
|
border-radius: 999px;
|
|
overflow: hidden;
|
|
flex-shrink: 0;
|
|
background: linear-gradient(145deg, #5eead4, #0f766e);
|
|
box-shadow: 0 0 0 2px #fff, 0 0 0 3px #ccfbf1;
|
|
}
|
|
|
|
img {
|
|
width: 100%;
|
|
height: 100%;
|
|
object-fit: cover;
|
|
display: block;
|
|
}
|
|
|
|
em {
|
|
font-style: normal;
|
|
color: #fff;
|
|
font-weight: 600;
|
|
font-size: 0.62rem;
|
|
line-height: 1;
|
|
}
|
|
</style>
|