forked from npmx-dev/npmx.dev
-
Notifications
You must be signed in to change notification settings - Fork 0
Expand file tree
/
Copy path[...package].vue
More file actions
128 lines (113 loc) · 4.27 KB
/
[...package].vue
File metadata and controls
128 lines (113 loc) · 4.27 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
<script setup lang="ts">
definePageMeta({
name: 'analytics',
})
const route = useRoute('analytics')
const router = useRouter()
// Extract package name from route params (handles scoped packages like @org/pkg)
const packageName = computed(() => {
const params = route.params.package || []
return params.join('/')
})
// Fetch package data to get creation date and check if package exists
const { data: packument, status: packageStatus } = usePackage(packageName)
const createdIso = computed(() => packument.value?.time?.created ?? null)
// Determine if package exists (has data after loading)
const packageExists = computed(() => packageStatus.value === 'success' && !!packument.value)
// Close destination: package page if exists, home otherwise
const closeDestination = computed(() => (packageExists.value ? `/${packageName.value}` : '/'))
// Fetch weekly downloads for initial display
const { fetchPackageDownloadEvolution } = useCharts()
const weeklyDownloads = shallowRef<WeeklyDownloadPoint[]>([])
async function loadWeeklyDownloads() {
if (!import.meta.client) return
if (!packageName.value) return
try {
const result = await fetchPackageDownloadEvolution(
() => packageName.value,
() => createdIso.value,
() => ({ granularity: 'week' as const, weeks: 52 }),
)
weeklyDownloads.value = (result as WeeklyDownloadPoint[]) ?? []
} catch {
weeklyDownloads.value = []
}
}
onMounted(() => {
loadWeeklyDownloads()
})
watch(packageName, () => loadWeeklyDownloads())
function handleKeydown(event: KeyboardEvent) {
if (event.key === 'Escape') {
router.push(closeDestination.value)
}
}
// SEO
useSeoMeta({
title: () => (packageName.value ? `${packageName.value} Downloads - npmx` : 'Downloads - npmx'),
description: () => `Download statistics and trends for ${packageName.value}`,
})
</script>
<template>
<Teleport to="body">
<div
class="fixed inset-0 z-50 flex items-center justify-center p-0 sm:p-4"
@keydown="handleKeydown"
>
<!-- Backdrop - clicking navigates back -->
<NuxtLink
:to="closeDestination"
class="absolute inset-0 bg-black/60 cursor-default"
:aria-label="$t('common.close_modal')"
/>
<div
class="relative w-full h-full sm:h-auto bg-bg sm:border sm:border-border sm:rounded-lg shadow-xl sm:max-h-[90vh] overflow-y-auto overscroll-contain sm:max-w-3xl"
role="dialog"
aria-modal="true"
aria-labelledby="analytics-modal-title"
>
<div class="p-4 sm:p-6">
<div class="flex items-center justify-between mb-4 sm:mb-6">
<h1 id="analytics-modal-title" class="font-mono text-lg font-medium">
{{ $t('package.downloads.modal_title') }}
</h1>
<NuxtLink
:to="closeDestination"
class="text-fg-subtle hover:text-fg transition-colors duration-200 focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-fg/50 rounded"
:aria-label="$t('common.close')"
>
<span class="i-carbon-close block w-5 h-5" aria-hidden="true" />
</NuxtLink>
</div>
<div class="font-mono text-sm">
<ClientOnly>
<PackageDownloadAnalytics
:weeklyDownloads="weeklyDownloads"
:inModal="true"
:packageName="packageName"
:createdIso="createdIso"
/>
<template #fallback>
<div class="min-h-[260px] flex items-center justify-center">
<span
class="i-carbon:circle-dash w-6 h-6 motion-safe:animate-spin text-fg-subtle"
/>
</div>
</template>
</ClientOnly>
</div>
</div>
<!-- Mobile close button -->
<div class="sm:hidden flex justify-center pb-4">
<NuxtLink
:to="closeDestination"
class="w-12 h-12 bg-bg-elevated border border-border rounded-full shadow-lg flex items-center justify-center text-fg-muted hover:text-fg transition-colors"
:aria-label="$t('common.close')"
>
<span class="w-5 h-5 i-carbon:close" aria-hidden="true" />
</NuxtLink>
</div>
</div>
</div>
</Teleport>
</template>