Skip to content

Commit e167fb1

Browse files
committed
refactor: use defineModel
1 parent 7b952e2 commit e167fb1

6 files changed

Lines changed: 57 additions & 84 deletions

File tree

app/components/PackageList.vue

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -37,8 +37,6 @@ const props = defineProps<{
3737
viewMode?: ViewMode
3838
/** Column configuration for table view */
3939
columns?: ColumnConfig[]
40-
/** Sort option for table header sorting */
41-
sortOption?: SortOption
4240
/** Pagination mode: infinite or paginated */
4341
paginationMode?: PaginationMode
4442
/** Current page (1-indexed) for paginated mode */
@@ -61,6 +59,9 @@ const emit = defineEmits<{
6159
// Reference to WindowVirtualizer for infinite scroll detection
6260
const listRef = useTemplateRef<WindowVirtualizerHandle>('listRef')
6361
62+
/** Sort option for table header sorting */
63+
const sortOption = defineModel<SortOption>('sortOption')
64+
6465
// View mode and columns
6566
const viewMode = computed(() => props.viewMode ?? 'cards')
6667
const columns = computed(() => props.columns ?? DEFAULT_COLUMNS)
@@ -151,10 +152,9 @@ defineExpose({
151152
<PackageTable
152153
:results="displayedResults"
153154
:columns="columns"
154-
:sort-option="sortOption"
155+
v-model:sort-option="sortOption"
155156
:selected-index="selectedIndex"
156157
:is-loading="isLoading"
157-
@update:sort-option="emit('update:sortOption', $event)"
158158
@select="emit('select', $event)"
159159
@click-keyword="emit('clickKeyword', $event)"
160160
/>

app/components/PackageListToolbar.vue

Lines changed: 8 additions & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -23,11 +23,7 @@ import {
2323
2424
const props = defineProps<{
2525
filters: StructuredFilters
26-
sortOption: SortOption
27-
viewMode: ViewMode
2826
columns: ColumnConfig[]
29-
paginationMode: PaginationMode
30-
pageSize: PageSize
3127
totalCount: number
3228
filteredCount: number
3329
availableKeywords?: string[]
@@ -36,12 +32,12 @@ const props = defineProps<{
3632
searchContext?: boolean
3733
}>()
3834
35+
const sortOption = defineModel<SortOption>('sortOption', { required: true })
36+
const viewMode = defineModel<ViewMode>('viewMode', { required: true })
37+
const paginationMode = defineModel<PaginationMode>('paginationMode', { required: true })
38+
const pageSize = defineModel<PageSize>('pageSize', { required: true })
39+
3940
const emit = defineEmits<{
40-
'update:filters': [filters: StructuredFilters]
41-
'update:sortOption': [option: SortOption]
42-
'update:viewMode': [mode: ViewMode]
43-
'update:paginationMode': [mode: PaginationMode]
44-
'update:pageSize': [size: PageSize]
4541
'toggleColumn': [columnId: ColumnId]
4642
'resetColumns': []
4743
'clearFilter': [chip: FilterChip]
@@ -54,15 +50,10 @@ const emit = defineEmits<{
5450
'toggleKeyword': [keyword: string]
5551
}>()
5652
57-
const viewMode = computed({
58-
get: () => props.viewMode,
59-
set: (value: ViewMode) => emit('update:viewMode', value),
60-
})
61-
6253
const showingFiltered = computed(() => props.filteredCount !== props.totalCount)
6354
6455
// Parse current sort option into key and direction
65-
const currentSort = computed(() => parseSortOption(props.sortOption))
56+
const currentSort = computed(() => parseSortOption(sortOption.value))
6657
6758
// Get available sort keys based on context
6859
const availableSortKeys = computed(() => {
@@ -85,13 +76,13 @@ function handleSortKeyChange(event: Event) {
8576
const newKey = target.value as SortKey
8677
const config = SORT_KEYS.find(k => k.key === newKey)
8778
const direction = config?.defaultDirection ?? 'desc'
88-
emit('update:sortOption', buildSortOption(newKey, direction))
79+
sortOption.value = buildSortOption(newKey, direction)
8980
}
9081
9182
// Toggle sort direction
9283
function handleToggleDirection() {
9384
const { key, direction } = currentSort.value
94-
emit('update:sortOption', buildSortOption(key, toggleDirection(direction)))
85+
sortOption.value = buildSortOption(key, toggleDirection(direction))
9586
}
9687
9788
// Map sort key to i18n key

app/components/PackageTable.vue

Lines changed: 8 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -6,15 +6,15 @@ import { buildSortOption, parseSortOption, toggleDirection } from '#shared/types
66
const props = defineProps<{
77
results: NpmSearchResult[]
88
columns: ColumnConfig[]
9-
sortOption?: SortOption
109
selectedIndex?: number
1110
isLoading?: boolean
1211
}>()
1312
13+
const sortOption = defineModel<SortOption>('sortOption')
14+
1415
const emit = defineEmits<{
15-
'update:sortOption': [option: SortOption]
16-
'select': [index: number]
17-
'clickKeyword': [keyword: string]
16+
select: [index: number]
17+
clickKeyword: [keyword: string]
1818
}>()
1919
2020
function isColumnVisible(id: string): boolean {
@@ -48,14 +48,14 @@ const columnDefaultDirection: Record<string, 'asc' | 'desc'> = {
4848
}
4949
5050
function isColumnSorted(id: string): boolean {
51-
const option = props.sortOption
51+
const option = sortOption.value
5252
if (!option) return false
5353
const { key } = parseSortOption(option)
5454
return key === columnToSortKey[id]
5555
}
5656
5757
function getSortDirection(id: string): 'asc' | 'desc' | null {
58-
const option = props.sortOption
58+
const option = sortOption.value
5959
if (!option) return null
6060
if (!isColumnSorted(id)) return null
6161
const { direction } = parseSortOption(option)
@@ -73,11 +73,11 @@ function toggleSort(id: string) {
7373
if (!isSorted) {
7474
// First click - use default direction
7575
const defaultDir = columnDefaultDirection[id] ?? 'desc'
76-
emit('update:sortOption', buildSortOption(sortKey, defaultDir))
76+
sortOption.value = buildSortOption(sortKey, defaultDir)
7777
} else {
7878
// Toggle direction
7979
const currentDir = getSortDirection(id) ?? 'desc'
80-
emit('update:sortOption', buildSortOption(sortKey, toggleDirection(currentDir)))
80+
sortOption.value = buildSortOption(sortKey, toggleDirection(currentDir))
8181
}
8282
}
8383

app/components/PaginationControls.vue

Lines changed: 20 additions & 25 deletions
Original file line numberDiff line numberDiff line change
@@ -3,33 +3,28 @@ import type { PageSize, PaginationMode, ViewMode } from '#shared/types/preferenc
33
import { PAGE_SIZE_OPTIONS } from '#shared/types/preferences'
44
55
const props = defineProps<{
6-
mode: PaginationMode
7-
pageSize: PageSize
8-
currentPage: number
96
totalItems: number
107
/** When in table view, force pagination mode (no infinite scroll for tables) */
118
viewMode?: ViewMode
129
}>()
1310
11+
const mode = defineModel<PaginationMode>('mode', { required: true })
12+
const pageSize = defineModel<PageSize>('pageSize', { required: true })
13+
const currentPage = defineModel<number>('currentPage', { required: true })
14+
1415
// Whether we should show pagination controls (table view always uses pagination)
15-
const shouldShowControls = computed(() => props.viewMode === 'table' || props.mode === 'paginated')
16+
const shouldShowControls = computed(() => props.viewMode === 'table' || mode.value === 'paginated')
1617
1718
// Table view forces pagination mode, otherwise use the provided mode
1819
const effectiveMode = computed<PaginationMode>(() =>
1920
shouldShowControls.value ? 'paginated' : 'infinite',
2021
)
2122
22-
const emit = defineEmits<{
23-
'update:mode': [mode: PaginationMode]
24-
'update:pageSize': [size: PageSize]
25-
'update:currentPage': [page: number]
26-
}>()
27-
2823
// When 'all' is selected, there's only 1 page with everything
29-
const isShowingAll = computed(() => props.pageSize === 'all')
30-
const effectivePageSize = computed(() => (isShowingAll.value ? props.totalItems : props.pageSize))
24+
const isShowingAll = computed(() => pageSize.value === 'all')
25+
const effectivePageSize = computed(() => (isShowingAll.value ? props.totalItems : pageSize.value))
3126
const totalPages = computed(() =>
32-
isShowingAll.value ? 1 : Math.ceil(props.totalItems / (props.pageSize as number)),
27+
isShowingAll.value ? 1 : Math.ceil(props.totalItems / (pageSize.value as number)),
3328
)
3429
3530
// Whether to show the mode toggle (hidden in table view since table always uses pagination)
@@ -38,39 +33,39 @@ const showModeToggle = computed(() => props.viewMode !== 'table')
3833
const startItem = computed(() => {
3934
if (props.totalItems === 0) return 0
4035
if (isShowingAll.value) return 1
41-
return (props.currentPage - 1) * (props.pageSize as number) + 1
36+
return (currentPage.value - 1) * (pageSize.value as number) + 1
4237
})
4338
4439
const endItem = computed(() => {
4540
if (isShowingAll.value) return props.totalItems
46-
return Math.min(props.currentPage * (props.pageSize as number), props.totalItems)
41+
return Math.min(currentPage.value * (pageSize.value as number), props.totalItems)
4742
})
4843
49-
const canGoPrev = computed(() => props.currentPage > 1)
50-
const canGoNext = computed(() => props.currentPage < totalPages.value)
44+
const canGoPrev = computed(() => currentPage.value > 1)
45+
const canGoNext = computed(() => currentPage.value < totalPages.value)
5146
5247
function goToPage(page: number) {
5348
if (page >= 1 && page <= totalPages.value) {
54-
emit('update:currentPage', page)
49+
currentPage.value = page
5550
}
5651
}
5752
5853
function goPrev() {
5954
if (canGoPrev.value) {
60-
emit('update:currentPage', props.currentPage - 1)
55+
currentPage.value = currentPage.value - 1
6156
}
6257
}
6358
6459
function goNext() {
6560
if (canGoNext.value) {
66-
emit('update:currentPage', props.currentPage + 1)
61+
currentPage.value = currentPage.value + 1
6762
}
6863
}
6964
7065
// Generate visible page numbers with ellipsis
7166
const visiblePages = computed(() => {
7267
const total = totalPages.value
73-
const current = props.currentPage
68+
const current = currentPage.value
7469
const pages: (number | 'ellipsis')[] = []
7570
7671
if (total <= 7) {
@@ -112,9 +107,9 @@ function handlePageSizeChange(event: Event) {
112107
const value = target.value
113108
// Handle 'all' as a special string value, otherwise parse as number
114109
const newSize = (value === 'all' ? 'all' : Number(value)) as PageSize
115-
emit('update:pageSize', newSize)
110+
pageSize.value = newSize
116111
// Reset to page 1 when changing page size
117-
emit('update:currentPage', 1)
112+
currentPage.value = 1
118113
}
119114
</script>
120115

@@ -138,7 +133,7 @@ function handlePageSizeChange(event: Event) {
138133
class="px-2.5 py-1 text-xs font-mono rounded-sm transition-colors duration-200 focus-visible:ring-2 focus-visible:ring-fg focus-visible:ring-offset-1"
139134
:class="mode === 'infinite' ? 'bg-bg-muted text-fg' : 'text-fg-muted hover:text-fg'"
140135
:aria-pressed="mode === 'infinite'"
141-
@click="emit('update:mode', 'infinite')"
136+
@click="mode = 'infinite'"
142137
>
143138
{{ $t('filters.pagination.infinite') }}
144139
</button>
@@ -147,7 +142,7 @@ function handlePageSizeChange(event: Event) {
147142
class="px-2.5 py-1 text-xs font-mono rounded-sm transition-colors duration-200 focus-visible:ring-2 focus-visible:ring-fg focus-visible:ring-offset-1"
148143
:class="mode === 'paginated' ? 'bg-bg-muted text-fg' : 'text-fg-muted hover:text-fg'"
149144
:aria-pressed="mode === 'paginated'"
150-
@click="emit('update:mode', 'paginated')"
145+
@click="mode = 'paginated'"
151146
>
152147
{{ $t('filters.pagination.paginated') }}
153148
</button>

app/pages/@[org].vue

Lines changed: 8 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -223,17 +223,15 @@ defineOgImageComponent('Default', {
223223
<!-- Enhanced toolbar with filters -->
224224
<PackageListToolbar
225225
:filters="filters"
226-
:sort-option="sortOption"
227-
:view-mode="viewMode"
226+
v-model:sort-option="sortOption"
227+
v-model:view-mode="viewMode"
228228
:columns="columns"
229-
:pagination-mode="paginationMode"
230-
:page-size="pageSize"
229+
v-model:pagination-mode="paginationMode"
230+
v-model:page-size="pageSize"
231231
:total-count="packageCount"
232232
:filtered-count="filteredCount"
233233
:available-keywords="availableKeywords"
234234
:active-filters="activeFilters"
235-
@update:view-mode="viewMode = $event"
236-
@update:sort-option="setSort"
237235
@toggle-column="toggleColumn"
238236
@reset-columns="resetColumns"
239237
@clear-filter="handleClearFilter"
@@ -257,24 +255,20 @@ defineOgImageComponent('Default', {
257255
:results="sortedPackages"
258256
:view-mode="viewMode"
259257
:columns="columns"
260-
:sort-option="sortOption"
258+
v-model:sort-option="sortOption"
261259
:pagination-mode="paginationMode"
262260
:page-size="pageSize"
263261
:current-page="currentPage"
264-
@update:sort-option="handleSortChange"
265262
@click-keyword="toggleKeyword"
266263
/>
267264

268265
<!-- Pagination controls -->
269266
<PaginationControls
270-
:mode="paginationMode"
271-
:page-size="pageSize"
272-
:current-page="currentPage"
267+
v-model:mode="paginationMode"
268+
v-model:page-size="pageSize"
269+
v-model:current-page="currentPage"
273270
:total-items="sortedPackages.length"
274271
:view-mode="viewMode"
275-
@update:mode="paginationMode = $event"
276-
@update:page-size="pageSize = $event"
277-
@update:current-page="currentPage = $event"
278272
/>
279273
</template>
280274
</section>

app/pages/search.vue

Lines changed: 9 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -874,18 +874,16 @@ defineOgImageComponent('Default', {
874874
<div v-if="visibleResults.total > 0" class="mb-6">
875875
<PackageListToolbar
876876
:filters="filters"
877-
:sort-option="sortOption"
878-
:view-mode="viewMode"
877+
v-model:sort-option="sortOption"
878+
v-model:view-mode="viewMode"
879879
:columns="columns"
880-
:pagination-mode="paginationMode"
881-
:page-size="preferredPageSize"
880+
v-model:pagination-mode="paginationMode"
881+
v-model:page-size="preferredPageSize"
882882
:total-count="visibleResults.total"
883883
:filtered-count="displayResults.length"
884884
:available-keywords="availableKeywords"
885885
:active-filters="activeFilters"
886886
search-context
887-
@update:view-mode="viewMode = $event"
888-
@update:sort-option="setSort"
889887
@toggle-column="toggleColumn"
890888
@reset-columns="resetColumns"
891889
@clear-filter="handleClearFilter"
@@ -971,31 +969,26 @@ defineOgImageComponent('Default', {
971969
show-publisher
972970
:has-more="hasMore"
973971
:is-loading="isLoadingMore || (status === 'pending' && loadedPages > 1)"
974-
:page-size="pageSize"
972+
:page-size="preferredPageSize"
975973
:initial-page="initialPage"
976974
:view-mode="viewMode"
977975
:columns="columns"
978-
:sort-option="sortOption"
976+
v-model:sort-option="sortOption"
979977
:pagination-mode="paginationMode"
980978
:current-page="currentPage"
981979
@load-more="loadMore"
982980
@page-change="handlePageChange"
983981
@select="handlePackageSelect"
984-
@update:sort-option="handleSortChange"
985982
@click-keyword="toggleKeyword"
986983
/>
987984

988985
<!-- Pagination controls -->
989986
<PaginationControls
990987
v-if="displayResults.length > 0"
991-
:mode="paginationMode"
992-
:page-size="preferredPageSize"
993-
:current-page="currentPage"
994-
:total-items="displayResults.length"
988+
v-model:mode="paginationMode"
989+
v-model:page-size="preferredPageSize"
990+
v-model:current-page="currentPage"
995991
:view-mode="viewMode"
996-
@update:mode="paginationMode = $event"
997-
@update:page-size="preferredPageSize = $event"
998-
@update:current-page="currentPage = $event"
999992
/>
1000993
</div>
1001994
</section>

0 commit comments

Comments
 (0)