[READ-ONLY] a fast, modern browser for the npm registry
at main 120 lines 4.2 kB view raw
1<script setup lang="ts"> 2import type { NuxtLinkProps } from '#app' 3 4const props = withDefaults( 5 defineProps< 6 { 7 /** Disabled links will be displayed as plain text */ 8 disabled?: boolean 9 /** 10 * `type` should never be used, because this will always be a link. 11 * */ 12 type?: never 13 variant?: 'button-primary' | 'button-secondary' | 'link' 14 size?: 'small' | 'medium' 15 block?: boolean 16 17 ariaKeyshortcuts?: string 18 19 /** 20 * Don't use this directly. This will automatically be set to `_blank` for external links passed via `to`. 21 */ 22 target?: never 23 24 /** 25 * Don't use this directly. This will automatically be set for external links passed via `to`. 26 */ 27 rel?: never 28 29 classicon?: string 30 31 to?: NuxtLinkProps['to'] 32 33 /** always use `to` instead of `href` */ 34 href?: never 35 36 /** should only be used for links where the context makes it very clear they are clickable. Don't just use this, because you don't like underlines. */ 37 noUnderline?: boolean 38 } & NuxtLinkProps 39 >(), 40 { variant: 'link', size: 'medium' }, 41) 42 43const isLinkExternal = computed( 44 () => 45 !!props.to && 46 typeof props.to === 'string' && 47 (props.to.startsWith('http:') || props.to.startsWith('https:') || props.to.startsWith('//')), 48) 49const isLinkAnchor = computed( 50 () => !!props.to && typeof props.to === 'string' && props.to.startsWith('#'), 51) 52 53/** size is only applicable for button like links */ 54const isLink = computed(() => props.variant === 'link') 55const isButton = computed(() => !isLink.value) 56const isButtonSmall = computed(() => props.size === 'small' && !isLink.value) 57const isButtonMedium = computed(() => props.size === 'medium' && !isLink.value) 58</script> 59 60<template> 61 <span 62 v-if="disabled" 63 :class="{ 64 'flex': block, 65 'inline-flex': !block, 66 'opacity-50 gap-x-1 items-center justify-center font-mono border border-transparent rounded-md': 67 isButton, 68 'text-sm px-4 py-2': isButtonMedium, 69 'text-xs px-2 py-0.5': isButtonSmall, 70 'text-bg bg-fg': variant === 'button-primary', 71 'bg-transparent text-fg': variant === 'button-secondary', 72 }" 73 ><slot 74 /></span> 75 <NuxtLink 76 v-bind="props" 77 v-else 78 class="group/link gap-x-1 items-center" 79 :class="{ 80 'flex': block, 81 'inline-flex': !block, 82 'underline-offset-[0.2rem] underline decoration-1 decoration-fg/30': 83 !isLinkAnchor && isLink && !noUnderline, 84 'justify-start font-mono text-fg hover:(decoration-accent text-accent) focus-visible:(decoration-accent text-accent) transition-colors duration-200': 85 isLink, 86 'justify-center font-mono border border-border rounded-md transition-all duration-200': 87 isButton, 88 'text-sm px-4 py-2': isButtonMedium, 89 'text-xs px-2 py-0.5': isButtonSmall, 90 'bg-transparent text-fg hover:(bg-fg/10 text-accent) focus-visible:(bg-fg/10 text-accent) aria-[current=true]:(bg-fg/10 text-accent border-fg/20 hover:enabled:(bg-fg/20 text-fg/50))': 91 variant === 'button-secondary', 92 'text-bg bg-fg hover:(bg-fg/50 text-accent) focus-visible:(bg-fg/50) aria-current:(bg-fg text-bg border-fg hover:enabled:(text-bg/50))': 93 variant === 'button-primary', 94 }" 95 :to="to" 96 :aria-keyshortcuts="ariaKeyshortcuts" 97 :target="isLinkExternal ? '_blank' : undefined" 98 > 99 <span v-if="classicon" class="size-[1em]" :class="classicon" aria-hidden="true" /> 100 <slot /> 101 <!-- automatically show icon indicating external link --> 102 <span 103 v-if="isLinkExternal && !classicon" 104 class="i-lucide:external-link rtl-flip size-[1em] opacity-50" 105 aria-hidden="true" 106 /> 107 <span 108 v-else-if="isLinkAnchor && isLink" 109 class="i-lucide:link size-[1em] opacity-0 group-hover/link:opacity-100 transition-opacity duration-200" 110 aria-hidden="true" 111 /> 112 <kbd 113 v-if="ariaKeyshortcuts" 114 class="ms-2 inline-flex items-center justify-center size-4 text-xs text-fg bg-bg-muted border border-border rounded no-underline" 115 aria-hidden="true" 116 > 117 {{ ariaKeyshortcuts }} 118 </kbd> 119 </NuxtLink> 120</template>