82 lines
2.1 KiB
Vue
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>
|