93 lines
2.6 KiB
Vue
93 lines
2.6 KiB
Vue
<template>
|
|
<div class="avatar">
|
|
<img v-if="src" :src="src" :alt="alt" class="avatar-img" @error="handleImageError" />
|
|
<span v-else-if="initials" class="avatar-initials">{{ initials }}</span>
|
|
<slot v-else></slot>
|
|
</div>
|
|
</template>
|
|
|
|
<script lang="ts">
|
|
import { defineComponent, ref, watch } from 'vue';
|
|
|
|
export default defineComponent({
|
|
name: 'VAvatar',
|
|
props: {
|
|
src: {
|
|
type: String,
|
|
default: null,
|
|
},
|
|
initials: {
|
|
type: String,
|
|
default: null,
|
|
},
|
|
alt: {
|
|
type: String,
|
|
default: 'Avatar',
|
|
},
|
|
},
|
|
setup(props, { slots }) {
|
|
// Optional: Handle image loading errors, e.g., to show initials or slot content as a fallback
|
|
const imageError = ref(false);
|
|
const handleImageError = () => {
|
|
imageError.value = true;
|
|
};
|
|
|
|
watch(() => props.src, (newSrc) => {
|
|
if (newSrc) {
|
|
imageError.value = false; // Reset error state when src changes
|
|
}
|
|
});
|
|
|
|
// This computed prop is not strictly necessary for the template logic above,
|
|
// but can be useful if template logic becomes more complex or for debugging.
|
|
const showImage = computed(() => props.src && !imageError.value);
|
|
const showInitials = computed(() => !showImage.value && props.initials);
|
|
const showSlot = computed(() => !showImage.value && !showInitials.value && slots.default);
|
|
|
|
|
|
return {
|
|
handleImageError,
|
|
// expose computed if needed by a more complex template
|
|
// showImage,
|
|
// showInitials,
|
|
// showSlot,
|
|
};
|
|
},
|
|
});
|
|
</script>
|
|
|
|
<style lang="scss" scoped>
|
|
.avatar {
|
|
display: inline-flex;
|
|
align-items: center;
|
|
justify-content: center;
|
|
width: 40px; // Default size, can be made a prop or customized via CSS
|
|
height: 40px; // Default size
|
|
border-radius: 50%;
|
|
background-color: #E9ECEF; // Placeholder background, customize as needed (e.g., Gray-200)
|
|
color: #495057; // Placeholder text color (e.g., Gray-700)
|
|
font-weight: 500;
|
|
overflow: hidden; // Ensure content (like images) is clipped to the circle
|
|
vertical-align: middle; // Better alignment with surrounding text/elements
|
|
|
|
.avatar-img {
|
|
width: 100%;
|
|
height: 100%;
|
|
object-fit: cover; // Ensures the image covers the area without distortion
|
|
}
|
|
|
|
.avatar-initials {
|
|
font-size: 0.9em; // Adjust based on avatar size and desired text appearance
|
|
line-height: 1; // Ensure initials are centered vertically
|
|
text-transform: uppercase;
|
|
}
|
|
|
|
// If using an icon via slot, you might want to style it too
|
|
// Example:
|
|
// ::v-deep(svg), ::v-deep(.icon) { // if slot contains an icon component or raw svg
|
|
// width: 60%;
|
|
// height: 60%;
|
|
// }
|
|
}
|
|
</style>
|