Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

feat(ui): NPagination component #444

Draft
wants to merge 1 commit into
base: main
Choose a base branch
from
Draft
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
110 changes: 110 additions & 0 deletions packages/devtools-ui-kit/src/components/NPagination.vue
Original file line number Diff line number Diff line change
@@ -0,0 +1,110 @@
<script setup lang="ts">
import { computed } from 'vue'
import { useVModel } from '@vueuse/core'

const props = withDefaults(defineProps<{
modelValue: number
total: number
pageCount?: number
max?: number
divider?: string
}>(), {
pageCount: 5,
divider: '...',
max: 10,
})

const emit = defineEmits<{ (...args: any): void }>()
const currentPage = useVModel(props, 'modelValue', emit, { passive: true })

const pages = computed(() => Array.from({ length: Math.ceil(props.total / props.pageCount) }, (_, i) => i + 1))

const displayedPages = computed(() => {
if (!props.max || pages.value.length <= 5) {
return pages.value
}
else {
const current = currentPage.value
const max = pages.value.length
const r = Math.floor((Math.min(props.max, max) - 5) / 2)
const r1 = current - r
const r2 = current + r
const beforeWrapped = r1 - 1 > 1
const afterWrapped = r2 + 1 < max
const items: Array<number | string> = [1]

if (beforeWrapped)
items.push(props.divider)

if (!afterWrapped) {
const addedItems = (current + r + 2) - max
for (let i = current - r - addedItems; i <= current - r - 1; i++)
items.push(i)
}

for (let i = r1 > 2 ? (r1) : 2; i <= Math.min(max, r2); i++)
items.push(i)

if (!beforeWrapped) {
const addedItems = 1 - (current - r - 2)
for (let i = current + r + 1; i <= current + r + addedItems; i++)
items.push(i)
}

if (afterWrapped)
items.push(props.divider)

if (r2 < max)
items.push(max)

// Replace divider by number on start edge case [1, '…', 3, ...]
if (items.length >= 3 && items[1] === props.divider && items[2] === 3)
items[1] = 2

// Replace divider by number on end edge case [..., 48, '…', 50]
if (items.length >= 3 && items[items.length - 2] === props.divider && items[items.length - 1] === items.length)
items[items.length - 2] = items.length - 1

return items
}
})

const hasPrev = computed(() => currentPage.value > 1)
const hasNext = computed(() => currentPage.value < pages.value.length)

function clickNext() {
if (!hasNext.value)
return
currentPage.value++
}
function clickPrev() {
if (!hasPrev.value)
return
currentPage.value--
}
</script>

<template>
<div flex="~ ">
<slot name="prev" @click="clickPrev">
<NButton rounded="l none" icon="carbon-chevron-left" @click="clickPrev" />
</slot>

<NButton
v-for="(page, index) of displayedPages"
:key="`${page}-${index}`"
rounded-none
:class="[
{ 'pointer-events-none': typeof page === 'string' },
{ 'border-primary text-primary': page === currentPage },
]"
:n="page === currentPage ? 'primary' : ''"
@click="currentPage = page as number"
v-text="page"
/>

<slot name="next" @click="clickNext">
<NButton rounded="r none" icon="carbon-chevron-right" @click="clickNext" />
</slot>
</div>
</template>