| 1 | <script setup lang="ts"> |
| 2 | import { useData, withBase } from 'vitepress' |
| 3 | import { computed } from 'vue' |
| 4 | |
| 5 | const props = defineProps<{ |
| 6 | tag: string |
| 7 | removable?: boolean |
| 8 | noclick?: boolean |
| 9 | }>() |
| 10 | const emit = defineEmits(['remove']) |
| 11 | |
| 12 | const { isDark } = useData() |
| 13 | |
| 14 | function getHashColorFromString( |
| 15 | name: string, |
| 16 | opacity: number | string = 1, |
| 17 | ) { |
| 18 | name += 'salt' |
| 19 | let hash = 0 |
| 20 | for (let i = 0; i < name.length; i++) |
| 21 | hash = name.charCodeAt(i) + ((hash << 5) - hash) |
| 22 | const h = hash % 360 |
| 23 | return getHsla(h, opacity) |
| 24 | } |
| 25 | |
| 26 | function getHsla( |
| 27 | hue: number, |
| 28 | opacity: number | string = 1, |
| 29 | ) { |
| 30 | const saturation = isDark.value ? 50 : 65 |
| 31 | const lightness = isDark.value ? 60 : 40 |
| 32 | return `hsla(${hue}, ${saturation}%, ${lightness}%, ${opacity})` |
| 33 | } |
| 34 | |
| 35 | const formattedTag = computed(() => { |
| 36 | let tag = props.tag |
| 37 | if (tag === tag.toUpperCase()) { |
| 38 | return tag |
| 39 | } |
| 40 | tag = tag.replace(/\b(API|CLI)\b/gi, m => m.toUpperCase()) |
| 41 | .replace(/-/g, ' ') |
| 42 | return tag[0].toUpperCase() + tag.slice(1) |
| 43 | }) |
| 44 | |
| 45 | const colors = computed(() => { |
| 46 | return [ |
| 47 | getHashColorFromString(props.tag), |
| 48 | getHashColorFromString(props.tag, 0.7), |
| 49 | getHashColorFromString(props.tag, 0.5), |
| 50 | getHashColorFromString(props.tag, 0.2), |
| 51 | getHashColorFromString(props.tag, 0.1), |
| 52 | ] |
| 53 | }) |
| 54 | </script> |
| 55 | |
| 56 | <template> |
| 57 | <a v-if="props.removable" class="feature-tag flex gap-1 items-center"> |
| 58 | {{ formattedTag }} |
| 59 | <button class="flex items-center op-70 hover:bg-gray-200/20 hover:op90 rounded-full mr--1" @click="emit('remove')"> |
| 60 | <div class="i-carbon:close" /> |
| 61 | </button> |
| 62 | </a> |
| 63 | <span v-else-if="props.noclick" class="feature-tag"> |
| 64 | {{ formattedTag }} |
| 65 | </span> |
| 66 | <a v-else class="feature-tag" :href="withBase(`/features/#tags=${tag}`)"> |
| 67 | {{ formattedTag }} |
| 68 | </a> |
| 69 | </template> |
| 70 | |
| 71 | <style scoped> |
| 72 | .feature-tag { |
| 73 | --uno: 'text-sm px-2 py-.5 rounded-md select-none !decoration-none border border-solid h-max'; |
| 74 | background-color: v-bind('colors[4]'); |
| 75 | color: v-bind('colors[0]') !important; |
| 76 | border-color: v-bind('colors[3]'); |
| 77 | } |
| 78 | |
| 79 | .feature-tag:hover { |
| 80 | background-color: v-bind('colors[3]'); |
| 81 | } |
| 82 | </style> |
| 83 |