[READ-ONLY] a fast, modern browser for the npm registry
at main 136 lines 4.1 kB view raw
1<script setup lang="ts"> 2import type { PackageFileTree } from '#shared/types' 3import type { RouteLocationRaw } from 'vue-router' 4import type { RouteNamedMap } from 'vue-router/auto-routes' 5import { ADDITIONAL_ICONS, getFileIcon } from '~/utils/file-icons' 6 7const props = defineProps<{ 8 tree: PackageFileTree[] 9 currentPath: string 10 baseUrl: string 11 baseRoute: Pick<RouteNamedMap['code'], 'params'> 12}>() 13 14// Get the current directory's contents 15const currentContents = computed(() => { 16 if (!props.currentPath) { 17 return props.tree 18 } 19 20 const parts = props.currentPath.split('/') 21 let current: PackageFileTree[] | undefined = props.tree 22 23 for (const part of parts) { 24 const found: PackageFileTree | undefined = current?.find(n => n.name === part) 25 if (!found || found.type === 'file') { 26 return [] 27 } 28 current = found.children 29 } 30 31 return current ?? [] 32}) 33 34// Get parent directory path 35const parentPath = computed(() => { 36 if (!props.currentPath) return null 37 const parts = props.currentPath.split('/') 38 if (parts.length <= 1) return '' 39 return parts.slice(0, -1).join('/') 40}) 41 42// Build route object for a path 43function getCodeRoute(nodePath?: string): RouteLocationRaw { 44 return { 45 name: 'code', 46 params: { 47 org: props.baseRoute.params.org, 48 packageName: props.baseRoute.params.packageName, 49 version: props.baseRoute.params.version, 50 filePath: nodePath ?? '', 51 }, 52 } 53} 54 55const bytesFormatter = useBytesFormatter() 56</script> 57 58<template> 59 <div class="directory-listing"> 60 <!-- Empty state --> 61 <div v-if="currentContents.length === 0" class="py-20 text-center text-fg-muted"> 62 <p>{{ $t('code.no_files') }}</p> 63 </div> 64 65 <!-- File list --> 66 <table v-else class="w-full"> 67 <thead class="sr-only"> 68 <tr> 69 <th>{{ $t('code.table.name') }}</th> 70 <th>{{ $t('code.table.size') }}</th> 71 </tr> 72 </thead> 73 <tbody> 74 <!-- Parent directory link --> 75 <tr 76 v-if="parentPath !== null" 77 class="border-b border-border hover:bg-bg-subtle transition-colors" 78 > 79 <td colspan="2"> 80 <LinkBase 81 :to="getCodeRoute(parentPath || undefined)" 82 class="py-2 px-4 font-mono text-sm w-full" 83 no-underline 84 > 85 <svg 86 class="size-[1em] me-1 shrink-0 text-yellow-600" 87 viewBox="0 0 16 16" 88 fill="currentColor" 89 aria-hidden="true" 90 > 91 <use :href="`/file-tree-sprite.svg#${ADDITIONAL_ICONS['folder']}`" /> 92 </svg> 93 <span class="w-full flex justify-self-stretch items-center gap-2"> .. </span> 94 </LinkBase> 95 </td> 96 </tr> 97 98 <!-- Directory/file rows --> 99 <tr 100 v-for="node in currentContents" 101 :key="node.path" 102 class="border-b border-border hover:bg-bg-subtle transition-colors" 103 > 104 <td colspan="2"> 105 <LinkBase 106 :to="getCodeRoute(node.path)" 107 class="py-2 px-4 font-mono text-sm w-full" 108 no-underline 109 > 110 <svg 111 class="size-[1em] me-1 shrink-0" 112 viewBox="0 0 16 16" 113 fill="currentColor" 114 :class="node.type === 'directory' ? 'text-yellow-600' : undefined" 115 aria-hidden="true" 116 > 117 <use 118 :href="`/file-tree-sprite.svg#${node.type === 'directory' ? ADDITIONAL_ICONS['folder'] : getFileIcon(node.name)}`" 119 /> 120 </svg> 121 <span class="w-full flex justify-self-stretch items-center gap-2"> 122 <span class="flex-1">{{ node.name }}</span> 123 <span 124 v-if="node.type === 'file' && node.size" 125 class="text-end text-xs text-fg-subtle" 126 > 127 {{ bytesFormatter.format(node.size) }} 128 </span> 129 </span> 130 </LinkBase> 131 </td> 132 </tr> 133 </tbody> 134 </table> 135 </div> 136</template>