forked from
npmx.dev/npmx.dev
[READ-ONLY]
a fast, modern browser for the npm registry
1<script setup lang="ts">
2const route = useRoute()
3const router = useRouter()
4const { searchProvider } = useSearchProvider()
5const searchProviderValue = computed(() => {
6 const p = normalizeSearchParam(route.query.p)
7 if (p === 'npm' || searchProvider.value === 'npm') return 'npm'
8 return 'algolia'
9})
10
11const isOpen = shallowRef(false)
12const toggleRef = useTemplateRef('toggleRef')
13
14onClickOutside(toggleRef, () => {
15 isOpen.value = false
16})
17
18useEventListener('keydown', event => {
19 if (event.key === 'Escape' && isOpen.value) {
20 isOpen.value = false
21 }
22})
23</script>
24
25<template>
26 <div ref="toggleRef" class="relative">
27 <ButtonBase
28 :aria-label="$t('settings.data_source.label')"
29 :aria-expanded="isOpen"
30 aria-haspopup="true"
31 size="small"
32 class="border-none w-8 h-8 !px-0 justify-center"
33 classicon="i-lucide:settings"
34 @click="isOpen = !isOpen"
35 />
36
37 <Transition
38 enter-active-class="transition-all duration-150"
39 leave-active-class="transition-all duration-100"
40 enter-from-class="opacity-0 translate-y-1"
41 leave-to-class="opacity-0 translate-y-1"
42 >
43 <div
44 v-if="isOpen"
45 class="absolute inset-ie-0 top-full pt-2 w-72 z-50"
46 role="menu"
47 :aria-label="$t('settings.data_source.label')"
48 >
49 <div
50 class="bg-bg-subtle/80 backdrop-blur-sm border border-border-subtle rounded-lg shadow-lg shadow-bg-elevated/50 overflow-hidden p-1"
51 >
52 <!-- npm Registry option -->
53 <button
54 type="button"
55 role="menuitem"
56 class="w-full flex items-start gap-3 px-3 py-2.5 rounded-md text-start transition-colors hover:bg-bg-muted"
57 :class="[searchProviderValue !== 'algolia' ? 'bg-bg-muted' : '']"
58 @click="
59 () => {
60 searchProvider = 'npm'
61 router.push({ query: { ...route.query, p: 'npm' } })
62 isOpen = false
63 }
64 "
65 >
66 <span
67 class="i-simple-icons:npm w-4 h-4 mt-0.5 shrink-0"
68 :class="searchProviderValue !== 'algolia' ? 'text-accent' : 'text-fg-muted'"
69 aria-hidden="true"
70 />
71 <div class="min-w-0 flex-1">
72 <div
73 class="text-sm font-medium"
74 :class="searchProviderValue !== 'algolia' ? 'text-fg' : 'text-fg-muted'"
75 >
76 {{ $t('settings.data_source.npm') }}
77 </div>
78 <p class="text-xs text-fg-subtle mt-0.5">
79 {{ $t('settings.data_source.npm_description') }}
80 </p>
81 </div>
82 </button>
83
84 <!-- Algolia option -->
85 <button
86 type="button"
87 role="menuitem"
88 class="w-full flex items-start gap-3 px-3 py-2.5 rounded-md text-start transition-colors hover:bg-bg-muted mt-1"
89 :class="[searchProviderValue === 'algolia' ? 'bg-bg-muted' : '']"
90 @click="
91 () => {
92 searchProvider = 'algolia'
93 router.push({ query: { ...route.query, p: undefined } })
94 isOpen = false
95 }
96 "
97 >
98 <span
99 class="i-simple-icons:algolia w-4 h-4 mt-0.5 shrink-0"
100 :class="searchProviderValue === 'algolia' ? 'text-accent' : 'text-fg-muted'"
101 aria-hidden="true"
102 />
103 <div class="min-w-0 flex-1">
104 <div
105 class="text-sm font-medium"
106 :class="searchProviderValue === 'algolia' ? 'text-fg' : 'text-fg-muted'"
107 >
108 {{ $t('settings.data_source.algolia') }}
109 </div>
110 <p class="text-xs text-fg-subtle mt-0.5">
111 {{ $t('settings.data_source.algolia_description') }}
112 </p>
113 </div>
114 </button>
115
116 <!-- Algolia attribution -->
117 <div
118 v-if="searchProviderValue === 'algolia'"
119 class="border-t border-border mx-1 mt-1 pt-2 pb-1"
120 >
121 <a
122 href="https://www.algolia.com/developers"
123 target="_blank"
124 rel="noopener noreferrer"
125 class="text-xs text-fg-subtle hover:text-fg-muted transition-colors inline-flex items-center gap-1 px-2"
126 >
127 {{ $t('search.algolia_disclaimer') }}
128 <span class="i-lucide:external-link w-3 h-3" aria-hidden="true" />
129 </a>
130 </div>
131 </div>
132 </div>
133 </Transition>
134 </div>
135</template>