Files

82 lines
2.1 KiB
Vue

<script setup lang="ts">
import type { Technology } from '~~/shared/types'
import { techStack } from '~/data/techstack'
interface Props {
tech: Technology | string
showLevel?: boolean
showImage?: boolean
}
const props = withDefaults(defineProps<Props>(), {
showLevel: true,
showImage: true,
})
const techMapping: Record<string, string> = {
'Three.js': 'JavaScript',
'WebGL': 'JavaScript',
'Discord.js': 'JavaScript',
'Express': 'Node.js',
'Canvas': 'JavaScript',
'Insta.js': 'JavaScript',
'Instagram API': 'JavaScript',
'Crowdin API': 'JavaScript',
'Cron': 'Node.js',
}
const techData = computed((): Technology => {
if (typeof props.tech !== 'string') {
return props.tech
}
const techName = props.tech
const allTechs = Object.values(techStack).flat()
let found = allTechs.find((t) => t.name.toLowerCase() === techName.toLowerCase())
const mapped = techMapping[techName]
if (!found && mapped) {
found = allTechs.find((t) => t.name.toLowerCase() === mapped.toLowerCase())
}
return found ?? { name: techName, image: '', level: 'Intermediate' as const }
})
const levelColor = computed(() => {
switch (techData.value.level) {
case 'Advanced':
return 'success' as const
case 'Intermediate':
return 'primary' as const
case 'Beginner':
return 'neutral' as const
default:
return 'neutral' as const
}
})
</script>
<template>
<div
class="inline-flex items-center gap-2 px-3 py-1.5 rounded-lg bg-gray-50 dark:bg-gray-800 border border-gray-200 dark:border-gray-700/50 transition-colors hover:border-brand-500/30"
itemscope
itemtype="https://schema.org/ComputerLanguage"
>
<NuxtImg
v-if="showImage && techData.image"
:src="techData.image"
:alt="`${techData.name} logo`"
width="20"
height="20"
loading="lazy"
class="shrink-0"
itemprop="image"
/>
<span class="text-sm font-medium text-gray-700 dark:text-gray-300" itemprop="name">{{ techData.name }}</span>
<UBadge v-if="showLevel" :color="levelColor" variant="subtle" size="xs">
{{ techData.level }}
</UBadge>
</div>
</template>