81 lines
1.8 KiB
Vue
81 lines
1.8 KiB
Vue
<script setup>
|
|
import { computed } from 'vue';
|
|
|
|
const props = defineProps({
|
|
name: {
|
|
type: String,
|
|
default: '',
|
|
},
|
|
email: {
|
|
type: String,
|
|
default: '',
|
|
},
|
|
src: {
|
|
type: String,
|
|
default: null,
|
|
},
|
|
size: {
|
|
type: String,
|
|
default: 'md',
|
|
},
|
|
});
|
|
|
|
const initials = computed(() => {
|
|
const basis = props.name?.trim() || props.email?.trim() || '?';
|
|
const parts = basis.split(/[\s@._-]+/).filter(Boolean);
|
|
|
|
if (!parts.length) {
|
|
return '?';
|
|
}
|
|
|
|
if (parts.length === 1) {
|
|
return parts[0].slice(0, 2).toUpperCase();
|
|
}
|
|
|
|
return `${parts[0][0] ?? ''}${parts[1][0] ?? ''}`.toUpperCase();
|
|
});
|
|
|
|
const classes = computed(() => ({
|
|
avatar: true,
|
|
'avatar-sm': props.size === 'sm',
|
|
'avatar-md': props.size === 'md',
|
|
'avatar-lg': props.size === 'lg',
|
|
}));
|
|
</script>
|
|
|
|
<template>
|
|
<div :class="classes">
|
|
<img
|
|
v-if="src"
|
|
:src="src"
|
|
:alt="name || email || 'Avatar'"
|
|
/>
|
|
<span v-else>{{ initials }}</span>
|
|
</div>
|
|
</template>
|
|
|
|
<style scoped>
|
|
@reference "@/assets/main.css";
|
|
.avatar {
|
|
@apply inline-flex shrink-0 items-center justify-center overflow-hidden rounded-full font-black uppercase;
|
|
background: linear-gradient(135deg, rgba(15, 118, 110, 0.16) 0%, rgba(255, 138, 61, 0.18) 100%);
|
|
color: var(--app-color-on-surface);
|
|
}
|
|
|
|
.avatar img {
|
|
@apply h-full w-full object-cover;
|
|
}
|
|
|
|
.avatar-sm {
|
|
@apply h-9 w-9 text-xs;
|
|
}
|
|
|
|
.avatar-md {
|
|
@apply h-11 w-11 text-sm;
|
|
}
|
|
|
|
.avatar-lg {
|
|
@apply h-14 w-14 text-base;
|
|
}
|
|
</style>
|