[READ-ONLY] a fast, modern browser for the npm registry
at main 97 lines 3.7 kB view raw
1<script setup lang="ts"> 2const { 3 isFacetSelected, 4 toggleFacet, 5 selectCategory, 6 deselectCategory, 7 facetsByCategory, 8 categoryOrder, 9 getCategoryLabel, 10} = useFacetSelection() 11 12// Check if all non-comingSoon facets in a category are selected 13function isCategoryAllSelected(category: string): boolean { 14 const facets = facetsByCategory.value[category] ?? [] 15 const selectableFacets = facets.filter(f => !f.comingSoon) 16 return selectableFacets.length > 0 && selectableFacets.every(f => isFacetSelected(f.id)) 17} 18 19// Check if no facets in a category are selected 20function isCategoryNoneSelected(category: string): boolean { 21 const facets = facetsByCategory.value[category] ?? [] 22 const selectableFacets = facets.filter(f => !f.comingSoon) 23 return selectableFacets.length > 0 && selectableFacets.every(f => !isFacetSelected(f.id)) 24} 25</script> 26 27<template> 28 <div class="space-y-3" role="group" :aria-label="$t('compare.facets.group_label')"> 29 <div v-for="category in categoryOrder" :key="category"> 30 <!-- Category header with all/none buttons --> 31 <div class="flex items-center gap-2 mb-2"> 32 <span class="text-3xs text-fg-subtle uppercase tracking-wider"> 33 {{ getCategoryLabel(category) }} 34 </span> 35 <!-- TODO: These should be radios, since they are mutually exclusive, and currently this behavior is faked with buttons --> 36 <ButtonBase 37 :aria-label=" 38 $t('compare.facets.select_category', { category: getCategoryLabel(category) }) 39 " 40 :aria-pressed="isCategoryAllSelected(category)" 41 :disabled="isCategoryAllSelected(category)" 42 @click="selectCategory(category)" 43 size="small" 44 > 45 {{ $t('compare.facets.all') }} 46 </ButtonBase> 47 <span class="text-2xs text-fg-muted/40">/</span> 48 <ButtonBase 49 :aria-label=" 50 $t('compare.facets.deselect_category', { category: getCategoryLabel(category) }) 51 " 52 :aria-pressed="isCategoryNoneSelected(category)" 53 :disabled="isCategoryNoneSelected(category)" 54 @click="deselectCategory(category)" 55 size="small" 56 > 57 {{ $t('compare.facets.none') }} 58 </ButtonBase> 59 </div> 60 61 <!-- Facet buttons --> 62 <div class="flex items-center gap-1.5 flex-wrap" role="group"> 63 <!-- TODO: These should be checkboxes --> 64 <ButtonBase 65 v-for="facet in facetsByCategory[category]" 66 :key="facet.id" 67 size="small" 68 :title="facet.comingSoon ? $t('compare.facets.coming_soon') : facet.description" 69 :disabled="facet.comingSoon" 70 :aria-pressed="isFacetSelected(facet.id)" 71 :aria-label="facet.label" 72 class="gap-1 px-1.5 rounded transition-colors focus-visible:outline-accent/70" 73 :class=" 74 facet.comingSoon 75 ? 'text-fg-subtle/50 bg-bg-subtle border-border-subtle cursor-not-allowed' 76 : isFacetSelected(facet.id) 77 ? 'text-fg-muted bg-bg-muted' 78 : 'text-fg-subtle bg-bg-subtle border-border-subtle hover:text-fg-muted hover:border-border' 79 " 80 @click="!facet.comingSoon && toggleFacet(facet.id)" 81 :classicon=" 82 facet.comingSoon 83 ? undefined 84 : isFacetSelected(facet.id) 85 ? 'i-lucide:check' 86 : 'i-lucide:plus' 87 " 88 > 89 {{ facet.label }} 90 <span v-if="facet.comingSoon" class="text-4xs" 91 >({{ $t('compare.facets.coming_soon') }})</span 92 > 93 </ButtonBase> 94 </div> 95 </div> 96 </div> 97</template>