AuthorBadge.vue (3259B)
1 <template> 2 <a 3 v-if="link" 4 :href="link" 5 class="badge-link" 6 target="_blank" 7 rel="noopener noreferrer" 8 > 9 <span class="badge-container"> 10 <span class="badge-label">Author</span> 11 <span class="author-badge" :style="{ backgroundColor: getColor(author) }"> 12 <img :src="getAvatarUrl" :alt="author" :class="{ 'platform-avatar': !props.avatar }" class="author-avatar"> 13 {{ author }} 14 </span> 15 </span> 16 </a> 17 <span v-else class="badge-container"> 18 <span class="badge-label">Author</span> 19 <span class="author-badge" :style="{ backgroundColor: getColor(author) }"> 20 <img :src="getAvatarUrl" :alt="author" :class="{ 'platform-avatar': !props.avatar }" class="author-avatar"> 21 {{ author }} 22 </span> 23 </span> 24 </template> 25 26 <script setup> 27 import { computed } from 'vue' 28 29 const props = defineProps({ 30 author: { 31 type: String, 32 required: true 33 }, 34 avatar: { 35 type: String, 36 default: '' 37 }, 38 platform: { 39 type: String, 40 default: 'user' 41 }, 42 link: { 43 type: String, 44 default: '' 45 } 46 }) 47 48 // Platform avatars mapping 49 const platformAvatars = { 50 github: 'https://img.icons8.com/ios-filled/50/github.png', 51 gitlab: 'https://img.icons8.com/ios-filled/50/gitlab.png', 52 x: 'https://img.icons8.com/ios/50/twitterx--v2.png', 53 linkedin: 'https://img.icons8.com/ios-filled/50/linkedin.png', 54 bluesky: 'https://img.icons8.com/material-sharp/48/bluesky.png', 55 mastodon: 'https://img.icons8.com/windows/64/mastodon.png', 56 user: 'https://img.icons8.com/windows/64/user.png' 57 } 58 59 const getAvatarUrl = computed(() => { 60 // If custom avatar is provided, use it 61 if (props.avatar) { 62 return props.avatar 63 } 64 // If platform is specified, use platform avatar 65 if (props.platform && platformAvatars[props.platform.toLowerCase()]) { 66 return platformAvatars[props.platform.toLowerCase()] 67 } 68 // Default to user avatar 69 return platformAvatars.user 70 }) 71 72 // Color function remains the same 73 const getColor = (name) => { 74 const colors = [ 75 '#3eaf7c', // green 76 '#476582', // blue 77 '#c53e3e', // red 78 '#986801', // orange 79 '#8957e5' // purple 80 ] 81 const hash = name.split('').reduce((acc, char) => acc + char.charCodeAt(0), 0) 82 return colors[hash % colors.length] 83 } 84 </script> 85 86 <style scoped> 87 .badge-container { 88 display: inline-flex; 89 height: 20px; 90 line-height: 24px; 91 font-size: 12px; 92 font-weight: 500; 93 border-radius: 5px; 94 margin-right: 0.5rem; 95 margin-bottom: 0.5rem; 96 overflow: hidden; 97 font-family: -apple-system, BlinkMacSystemFont, "Segoe UI", Helvetica, Arial, sans-serif; 98 vertical-align: middle; 99 } 100 101 .badge-label { 102 padding: 0 8px; 103 background-color: #555; 104 color: white; 105 display: flex; 106 align-items: center; 107 } 108 109 .author-badge { 110 display: inline-flex; 111 align-items: center; 112 padding: 0 8px; 113 color: white; 114 } 115 116 .author-avatar { 117 width: 15px; 118 height: 15px; 119 border-radius: 50%; 120 margin-right: 0.25rem; 121 margin-left: -0.25rem; 122 } 123 .platform-avatar { 124 filter: brightness(0) invert(1); 125 } 126 .badge-link { 127 text-decoration: none; 128 color: inherit; 129 } 130 131 .badge-link:hover .author-badge { 132 opacity: 0.9; 133 } 134 135 .badge-link:hover .badge-container { 136 box-shadow: 0 0 0 1.25px rgba(248, 248, 247, 0.4); 137 transition: all 0.2s ease; 138 } 139 </style>