Skip to content

Commit a4c4128

Browse files
committed
feat: improve keywords navigation and ui
1 parent 5b0d173 commit a4c4128

6 files changed

Lines changed: 45 additions & 8 deletions

File tree

app/components/Package/Card.vue

Lines changed: 17 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,6 @@
11
<script setup lang="ts">
2+
import type { StructuredFilters } from '#shared/types/preferences'
3+
24
const props = defineProps<{
35
/** The search result object containing package data */
46
result: NpmSearchResult
@@ -8,10 +10,16 @@ const props = defineProps<{
810
showPublisher?: boolean
911
prefetch?: boolean
1012
index?: number
13+
/** Filters to apply to the results */
14+
filters?: StructuredFilters
1115
/** Search query for highlighting exact matches */
1216
searchQuery?: string
1317
}>()
1418
19+
const emit = defineEmits<{
20+
clickKeyword: [keyword: string]
21+
}>()
22+
1523
/** Check if this package is an exact match for the search query */
1624
const isExactMatch = computed(() => {
1725
if (!props.searchQuery) return false
@@ -160,18 +168,22 @@ const pkgDescription = useMarkdown(() => ({
160168
</div>
161169
</div>
162170

163-
<ul
171+
<div
164172
v-if="result.package.keywords?.length"
165173
:aria-label="$t('package.card.keywords')"
166174
class="relative z-10 flex flex-wrap gap-1.5 mt-3 pt-3 border-t border-border list-none m-0 p-0 pointer-events-none"
167175
>
168-
<li
176+
<button
169177
v-for="keyword in result.package.keywords.slice(0, 5)"
170178
:key="keyword"
171-
class="tag pointer-events-auto"
179+
type="button"
180+
class="tag text-xs hover:bg-fg hover:text-bg hover:border-fg transition-colors duration-200 focus-visible:ring-2 focus-visible:ring-fg focus-visible:ring-offset-1 pointer-events-auto"
181+
:class="{ 'bg-fg text-bg hover:opacity-80': props.filters?.keywords.includes(keyword) }"
182+
:title="`Filter by ${keyword}`"
183+
@click.stop="emit('clickKeyword', keyword)"
172184
>
173185
{{ keyword }}
174-
</li>
175-
</ul>
186+
</button>
187+
</div>
176188
</article>
177189
</template>

app/components/Package/List.vue

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -17,6 +17,8 @@ const SSR_COUNT = 20
1717
const props = defineProps<{
1818
/** List of search results to display */
1919
results: NpmSearchResult[]
20+
/** Filters to apply to the results */
21+
filters?: StructuredFilters
2022
/** Heading level for package names */
2123
headingLevel?: 'h2' | 'h3'
2224
/** Whether to show publisher username on cards */
@@ -153,6 +155,7 @@ defineExpose({
153155
<template v-if="viewMode === 'table'">
154156
<PackageTable
155157
:results="displayedResults"
158+
:filters="filters"
156159
:columns="columns"
157160
v-model:sort-option="sortOption"
158161
:is-loading="isLoading"
@@ -182,7 +185,9 @@ defineExpose({
182185
:index="index"
183186
:search-query="searchQuery"
184187
class="motion-safe:animate-fade-in motion-safe:animate-fill-both"
188+
:filters="filters"
185189
:style="{ animationDelay: `${Math.min(index * 0.02, 0.3)}s` }"
190+
@click-keyword="emit('clickKeyword', $event)"
186191
/>
187192
</div>
188193
</template>
@@ -199,6 +204,8 @@ defineExpose({
199204
:show-publisher="showPublisher"
200205
:index="index"
201206
:search-query="searchQuery"
207+
:filters="filters"
208+
@click-keyword="emit('clickKeyword', $event)"
202209
/>
203210
</div>
204211
</li>
@@ -231,6 +238,8 @@ defineExpose({
231238
:search-query="searchQuery"
232239
class="motion-safe:animate-fade-in motion-safe:animate-fill-both"
233240
:style="{ animationDelay: `${Math.min(index * 0.02, 0.3)}s` }"
241+
:filters="filters"
242+
@click-keyword="emit('clickKeyword', $event)"
234243
/>
235244
</li>
236245
</ol>

app/components/Package/Table.vue

Lines changed: 9 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,11 +1,18 @@
11
<script setup lang="ts">
22
import type { NpmSearchResult } from '#shared/types/npm-registry'
3-
import type { ColumnConfig, ColumnId, SortKey, SortOption } from '#shared/types/preferences'
3+
import type {
4+
ColumnConfig,
5+
ColumnId,
6+
SortKey,
7+
SortOption,
8+
StructuredFilters,
9+
} from '#shared/types/preferences'
410
import { buildSortOption, parseSortOption, toggleDirection } from '#shared/types/preferences'
511
612
const props = defineProps<{
713
results: NpmSearchResult[]
814
columns: ColumnConfig[]
15+
filters?: StructuredFilters
916
isLoading?: boolean
1017
}>()
1118
@@ -317,6 +324,7 @@ function getColumnLabelKey(id: ColumnId): string {
317324
:result="result"
318325
:columns="columns"
319326
:index="index"
327+
:filters="filters"
320328
@click-keyword="emit('clickKeyword', $event)"
321329
/>
322330
</template>

app/components/Package/TableRow.vue

Lines changed: 8 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,11 +1,12 @@
11
<script setup lang="ts">
22
import type { NpmSearchResult } from '#shared/types/npm-registry'
3-
import type { ColumnConfig } from '#shared/types/preferences'
3+
import type { ColumnConfig, StructuredFilters } from '#shared/types/preferences'
44
55
const props = defineProps<{
66
result: NpmSearchResult
77
columns: ColumnConfig[]
88
index?: number
9+
filters?: StructuredFilters
910
}>()
1011
1112
const emit = defineEmits<{
@@ -117,12 +118,17 @@ const allMaintainersText = computed(() => {
117118

118119
<!-- Keywords -->
119120
<td v-if="isColumnVisible('keywords')" class="py-2 px-3">
120-
<div v-if="pkg.keywords?.length" class="flex flex-wrap gap-1">
121+
<div
122+
v-if="pkg.keywords?.length"
123+
class="flex flex-wrap gap-1"
124+
:aria-label="$t('package.card.keywords')"
125+
>
121126
<button
122127
v-for="keyword in pkg.keywords.slice(0, 3)"
123128
:key="keyword"
124129
type="button"
125130
class="tag text-xs hover:bg-fg hover:text-bg hover:border-fg transition-colors duration-200 focus-visible:ring-2 focus-visible:ring-fg focus-visible:ring-offset-1"
131+
:class="{ 'bg-fg text-bg hover:opacity-80': props.filters?.keywords.includes(keyword) }"
126132
:title="`Filter by ${keyword}`"
127133
@click.stop="emit('clickKeyword', keyword)"
128134
>

app/pages/@[org].vue

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -282,6 +282,7 @@ defineOgImageComponent('Default', {
282282
:results="sortedPackages"
283283
:view-mode="viewMode"
284284
:columns="columns"
285+
:filters="filters"
285286
v-model:sort-option="sortOption"
286287
:pagination-mode="paginationMode"
287288
:page-size="pageSize"

app/pages/search.vue

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -740,6 +740,7 @@ defineOgImageComponent('Default', {
740740
v-if="displayResults.length > 0"
741741
:results="displayResults"
742742
:search-query="query"
743+
:filters="filters"
743744
search-context
744745
heading-level="h2"
745746
show-publisher

0 commit comments

Comments
 (0)