feat: pagination go to button (#7158)

* feat: paginator go to button

- Added a way in the paginator to select the page number you want to go to instead of clicking the arrow's.

* localization

---------

Co-authored-by: Prospector <6166773+Prospector@users.noreply.github.com>
This commit is contained in:
Jeff
2026-08-20 20:12:21 +00:00
committed by GitHub
co-authored by Prospector
parent a85b8eed3f
commit bcd7133251
2 changed files with 100 additions and 8 deletions
+91 -8
View File
@@ -3,7 +3,8 @@
<template v-if="page > 1"> <template v-if="page > 1">
<ButtonLink <ButtonLink
v-if="linkFunction" v-if="linkFunction"
aria-label="Previous Page" v-tooltip="formatMessage(messages.previousPage)"
:aria-label="formatMessage(messages.previousPage)"
:href="linkFunction(page - 1)" :href="linkFunction(page - 1)"
type="quiet" type="quiet"
class="!w-9 !px-0 !rounded-full" class="!w-9 !px-0 !rounded-full"
@@ -11,7 +12,13 @@
> >
<ChevronLeftIcon aria-hidden="true" /> <ChevronLeftIcon aria-hidden="true" />
</ButtonLink> </ButtonLink>
<IconButton v-else label="Previous Page" type="quiet" @click="switchPage(page - 1)"> <IconButton
v-else
v-tooltip="formatMessage(messages.previousPage)"
:label="formatMessage(messages.previousPage)"
type="quiet"
@click="switchPage(page - 1)"
>
<ChevronLeftIcon aria-hidden="true" /> <ChevronLeftIcon aria-hidden="true" />
</IconButton> </IconButton>
</template> </template>
@@ -24,9 +31,35 @@
}" }"
class="page-number-container" class="page-number-container"
> >
<div v-if="item === '-'" class="rotate-90 grid place-content-center"> <form v-if="item === '-'" class="grid place-content-center" @submit.prevent="goToPage">
<EllipsisVerticalIcon /> <StyledInput
</div> v-if="showPageInput === index"
:ref="focusInput"
v-model="pageInput"
v-tooltip="formatMessage(messages.goToPage)"
type="number"
:min="1"
:max="props.count"
placeholder="..."
clamp
class="w-14"
:aria-label="formatMessage(messages.goToPage)"
@focusout="showPageInput = undefined"
@keydown.escape="showPageInput = undefined"
/>
<div v-else class="rotate-90">
<button
v-tooltip="formatMessage(messages.goToPage)"
type="button"
:aria-label="formatMessage(messages.goToPage)"
class="grid place-content-center"
@click="openPageInput(index)"
>
<EllipsisVerticalIcon aria-hidden="true" />
</button>
</div>
</form>
<template v-else> <template v-else>
<ButtonLink <ButtonLink
v-if="linkFunction" v-if="linkFunction"
@@ -57,7 +90,8 @@
<template v-if="page !== pages[pages.length - 1]"> <template v-if="page !== pages[pages.length - 1]">
<ButtonLink <ButtonLink
v-if="linkFunction" v-if="linkFunction"
aria-label="Next Page" v-tooltip="formatMessage(messages.nextPage)"
:aria-label="formatMessage(messages.nextPage)"
:href="linkFunction(page + 1)" :href="linkFunction(page + 1)"
type="quiet" type="quiet"
class="!w-9 !px-0 !rounded-full" class="!w-9 !px-0 !rounded-full"
@@ -65,7 +99,13 @@
> >
<ChevronRightIcon aria-hidden="true" /> <ChevronRightIcon aria-hidden="true" />
</ButtonLink> </ButtonLink>
<IconButton v-else label="Next Page" type="quiet" @click="switchPage(page + 1)"> <IconButton
v-else
v-tooltip="formatMessage(messages.nextPage)"
:label="formatMessage(messages.nextPage)"
type="quiet"
@click="switchPage(page + 1)"
>
<ChevronRightIcon aria-hidden="true" /> <ChevronRightIcon aria-hidden="true" />
</IconButton> </IconButton>
</template> </template>
@@ -73,14 +113,19 @@
</template> </template>
<script setup lang="ts"> <script setup lang="ts">
import { ChevronLeftIcon, ChevronRightIcon, EllipsisVerticalIcon } from '@modrinth/assets' import { ChevronLeftIcon, ChevronRightIcon, EllipsisVerticalIcon } from '@modrinth/assets'
import { computed } from 'vue' import { type ComponentPublicInstance, computed, ref } from 'vue'
import { defineMessages, useVIntl } from '#ui/composables/i18n.ts'
import { Button, ButtonLink, IconButton } from './buttons' import { Button, ButtonLink, IconButton } from './buttons'
import StyledInput from './StyledInput.vue'
const emit = defineEmits<{ const emit = defineEmits<{
'switch-page': [page: number] 'switch-page': [page: number]
}>() }>()
const { formatMessage } = useVIntl()
const props = withDefaults( const props = withDefaults(
defineProps<{ defineProps<{
page: number page: number
@@ -92,6 +137,8 @@ const props = withDefaults(
count: 1, count: 1,
}, },
) )
const showPageInput = ref<number | undefined>(undefined)
const pageInput = ref<number | undefined>(undefined)
const pages = computed(() => { const pages = computed(() => {
const pages: ('-' | number)[] = [] const pages: ('-' | number)[] = []
@@ -129,4 +176,40 @@ const pages = computed(() => {
function switchPage(newPage: number) { function switchPage(newPage: number) {
emit('switch-page', Math.min(Math.max(newPage, 1), props.count)) emit('switch-page', Math.min(Math.max(newPage, 1), props.count))
} }
function focusInput(element: Element | ComponentPublicInstance | null) {
if (element && 'focus' in element) {
const styledInput = element as InstanceType<typeof StyledInput>
styledInput.focus()
}
}
function openPageInput(index: number) {
pageInput.value = undefined
showPageInput.value = index
}
function goToPage() {
if (pageInput.value !== undefined && pageInput.value >= 1 && pageInput.value <= props.count) {
switchPage(pageInput.value)
}
showPageInput.value = undefined
pageInput.value = undefined
}
const messages = defineMessages({
goToPage: {
id: 'ui.pagination.go-to-page',
defaultMessage: 'Go to page',
},
previousPage: {
id: 'ui.pagination.previous-page',
defaultMessage: 'Previous page',
},
nextPage: {
id: 'ui.pagination.next-page',
defaultMessage: 'Next page',
},
})
</script> </script>
+9
View File
@@ -6530,6 +6530,15 @@
"ui.confirm-leave-modal.title": { "ui.confirm-leave-modal.title": {
"defaultMessage": "Leave page?" "defaultMessage": "Leave page?"
}, },
"ui.pagination.go-to-page": {
"defaultMessage": "Go to page"
},
"ui.pagination.next-page": {
"defaultMessage": "Next page"
},
"ui.pagination.previous-page": {
"defaultMessage": "Previous page"
},
"ui.stacked-admonitions.alert-count": { "ui.stacked-admonitions.alert-count": {
"defaultMessage": "{count, plural, one {# alert} other {# alerts}}" "defaultMessage": "{count, plural, one {# alert} other {# alerts}}"
}, },