forked from
npmx.dev/npmx.dev
[READ-ONLY]
a fast, modern browser for the npm registry
1<script setup lang="ts">
2import type { DependencyDepth } from '#shared/types'
3
4const props = defineProps<{
5 packageName: string
6 version: string
7}>()
8
9const { data: analysisData, status } = useDependencyAnalysis(
10 () => props.packageName,
11 () => props.version,
12)
13
14const isExpanded = shallowRef(false)
15const showAll = shallowRef(false)
16
17const hasDeprecated = computed(
18 () => analysisData.value?.deprecatedPackages && analysisData.value.deprecatedPackages.length > 0,
19)
20
21// Banner color - purple for deprecated
22const bannerColor = 'border-purple-600/40 bg-purple-500/10 text-purple-700 dark:text-purple-400'
23
24// Styling for each depth level
25const depthStyles = {
26 root: {
27 bg: 'bg-purple-500/5 border-is-2 border-is-purple-600',
28 text: 'text-fg',
29 },
30 direct: {
31 bg: 'bg-purple-500/5 border-is-2 border-is-purple-500',
32 text: 'text-fg-muted',
33 },
34 transitive: {
35 bg: 'bg-purple-500/5 border-is-2 border-is-purple-400',
36 text: 'text-fg-muted',
37 },
38} as const
39
40function getDepthStyle(depth: DependencyDepth) {
41 return depthStyles[depth] || depthStyles.transitive
42}
43</script>
44
45<template>
46 <section v-if="status === 'success' && hasDeprecated" class="relative">
47 <div class="rounded-lg border overflow-hidden" :class="bannerColor">
48 <!-- Header -->
49 <button
50 type="button"
51 class="w-full flex items-center justify-between gap-3 px-4 py-3 text-start transition-colors duration-200 hover:bg-white/5 focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-inset focus-visible:ring-accent/70"
52 :aria-expanded="isExpanded"
53 aria-controls="deprecated-tree-details"
54 @click="isExpanded = !isExpanded"
55 >
56 <span class="flex items-center gap-2 min-w-0">
57 <span class="i-lucide:octagon-alert w-4 h-4 shrink-0" aria-hidden="true" />
58 <span class="font-mono text-sm font-medium truncate">
59 {{ $t('package.deprecated.tree_found', analysisData!.deprecatedPackages.length) }}
60 </span>
61 </span>
62 <span
63 class="i-lucide:chevron-down w-4 h-4 transition-transform duration-200 shrink-0"
64 :class="{ 'rotate-180': isExpanded }"
65 aria-hidden="true"
66 />
67 </button>
68
69 <!-- Expandable details -->
70 <div
71 v-show="isExpanded"
72 id="deprecated-tree-details"
73 class="border-t border-border bg-bg-subtle"
74 >
75 <ul class="divide-y divide-border list-none m-0 p-0">
76 <li
77 v-for="pkg in analysisData!.deprecatedPackages.slice(0, showAll ? undefined : 5)"
78 :key="`${pkg.name}@${pkg.version}`"
79 class="px-4 py-3"
80 :class="getDepthStyle(pkg.depth).bg"
81 >
82 <div class="flex items-center gap-2 mb-1">
83 <!-- Path badge -->
84 <DependencyPathPopup v-if="pkg.path && pkg.path.length > 1" :path="pkg.path" />
85
86 <NuxtLink
87 :to="packageRoute(pkg.name, pkg.version)"
88 class="font-mono text-sm font-medium hover:underline truncate py-4"
89 :class="getDepthStyle(pkg.depth).text"
90 >
91 {{ pkg.name }}@{{ pkg.version }}
92 </NuxtLink>
93 </div>
94 <p class="text-xs text-fg-muted m-0 line-clamp-2">
95 {{ pkg.message }}
96 </p>
97 </li>
98 </ul>
99
100 <button
101 v-if="analysisData!.deprecatedPackages.length > 5 && !showAll"
102 type="button"
103 class="w-full px-4 py-2 text-xs font-mono text-fg-muted hover:text-fg border-t border-border transition-colors duration-200"
104 @click="showAll = true"
105 >
106 {{
107 $t('package.deprecated.show_all', { count: analysisData!.deprecatedPackages.length })
108 }}
109 </button>
110 </div>
111 </div>
112 </section>
113</template>