[READ-ONLY] a fast, modern browser for the npm registry
at main 90 lines 2.6 kB view raw
1<script setup lang="ts"> 2export type SortOption = 'downloads' | 'updated' | 'name-asc' | 'name-desc' 3 4const props = defineProps<{ 5 /** Current search/filter text */ 6 filter: string 7 /** Current sort option */ 8 sort: SortOption 9 /** Placeholder text for the search input */ 10 placeholder?: string 11 /** Total count of packages (before filtering) */ 12 totalCount?: number 13 /** Filtered count of packages */ 14 filteredCount?: number 15}>() 16 17const emit = defineEmits<{ 18 'update:filter': [value: string] 19 'update:sort': [value: SortOption] 20}>() 21 22const filterValue = computed({ 23 get: () => props.filter, 24 set: value => emit('update:filter', value), 25}) 26 27const sortValue = computed({ 28 get: () => props.sort, 29 set: value => emit('update:sort', value), 30}) 31 32const sortOptions = computed( 33 () => 34 [ 35 { value: 'downloads', label: $t('package.sort.downloads') }, 36 { value: 'updated', label: $t('package.sort.published') }, 37 { value: 'name-asc', label: $t('package.sort.name_asc') }, 38 { value: 'name-desc', label: $t('package.sort.name_desc') }, 39 ] as const, 40) 41 42// Show filter count when filtering is active 43const showFilteredCount = computed(() => { 44 return ( 45 props.filter && 46 props.filteredCount !== undefined && 47 props.totalCount !== undefined && 48 props.filteredCount !== props.totalCount 49 ) 50}) 51</script> 52 53<template> 54 <div class="flex flex-col sm:flex-row gap-3 mb-6"> 55 <!-- Filter input --> 56 <div class="flex-1 relative"> 57 <label for="package-filter" class="sr-only">{{ $t('package.list.filter_label') }}</label> 58 <div 59 class="absolute h-full w-10 flex items-center justify-center text-fg-subtle pointer-events-none" 60 aria-hidden="true" 61 > 62 <div class="i-lucide:search w-4 h-4" /> 63 </div> 64 <InputBase 65 id="package-filter" 66 v-model="filterValue" 67 type="search" 68 :placeholder="placeholder ?? $t('package.list.filter_placeholder')" 69 no-correct 70 class="w-full min-w-25 ps-10" 71 size="medium" 72 /> 73 </div> 74 75 <!-- Sort select --> 76 <SelectField 77 :label="$t('package.list.sort_label')" 78 hidden-label 79 id="package-sort" 80 class="relative shrink-0" 81 v-model="sortValue" 82 :items="sortOptions.map(option => ({ label: option.label, value: option.value }))" 83 /> 84 </div> 85 86 <!-- Filtered count indicator --> 87 <p v-if="showFilteredCount" class="text-fg-subtle text-xs font-mono mb-4"> 88 {{ $t('package.list.showing_count', { filtered: filteredCount, total: totalCount }) }} 89 </p> 90</template>