Skip to content
Snippets Groups Projects
Pagination.vue 3.21 KiB
Newer Older
  • Learn to ignore specific revisions
  • <template>
      <div v-if="pageNumbers.length > 1" class="tw-flex tw-justify-center">
        <div role="menubar" class="tw-flex">
          <button
            :class="BUTTON_CLASS"
            type="button"
            v-bind="getNavAttrs(modelValue === 1)"
            :aria-label="t('paginator.gotoFirst')"
            @click="setPage(1)"
          >
            <icon-system-uicons-push-left />
          </button>
          <button
            :class="BUTTON_CLASS"
            type="button"
            v-bind="getNavAttrs(modelValue === 1)"
            :aria-label="t('paginator.gotoPrev')"
            @click="setPage(modelValue - 1)"
          >
            <icon-system-uicons-chevron-left />
          </button>
          <template v-for="page in pageNumbers" :key="page">
            <button
              :class="[BUTTON_CLASS, { 'tw-bg-aura-primary tw-text-white': modelValue === page }]"
              type="button"
              :aria-current="modelValue === page ? 'page' : undefined"
              :aria-setsize="lastPage"
              :aria-posinset="page"
              :aria-checked="modelValue === page"
              :aria-label="t('paginator.goto', { page })"
              role="menuitemradio"
              @click="setPage(page)"
            >
              {{ page }}
            </button>
          </template>
          <button
            :class="BUTTON_CLASS"
            type="button"
            v-bind="getNavAttrs(modelValue === lastPage)"
            :aria-label="t('paginator.gotoNext')"
            @click="setPage(modelValue + 1)"
          >
            <icon-system-uicons-chevron-right />
          </button>
          <button
            :class="BUTTON_CLASS"
            type="button"
            v-bind="getNavAttrs(modelValue === lastPage)"
            :aria-label="t('paginator.gotoLast')"
            @click="setPage(lastPage)"
          >
            <icon-system-uicons-push-right />
          </button>
        </div>
      </div>
    </template>
    
    <script lang="ts" setup>
    import { computed } from 'vue'
    import { useI18n } from '@/i18n'
    
    const BUTTON_CLASS =
      'btn btn-default tw-border tw-border-solid tw-border-r-0 last:tw-border-r first:tw-rounded-l last:tw-rounded-r tw-rounded-none'
    
    const props = defineProps<{
      modelValue: number
      itemsPerPage: number
      count: number
      maxVisiblePages?: number
    }>()
    const emit = defineEmits<{
      (e: 'update:modelValue', value: number): void
    }>()
    const { t } = useI18n()
    
    const maxVisiblePages = computed(() => props.maxVisiblePages ?? 5)
    const lastPage = computed(() => Math.max(1, Math.ceil(props.count / props.itemsPerPage)))
    const pageNumbers = computed(() => {
      const result: number[] = []
      const pageOffset = Math.floor(maxVisiblePages.value / 2)
      let page = Math.max(1, props.modelValue - pageOffset)
      if (props.modelValue + pageOffset > lastPage.value)
        page = Math.max(1, page + lastPage.value - (props.modelValue + pageOffset))
      while (result.length < maxVisiblePages.value) {
        result.push(page)
        if (page === lastPage.value) break
        page++
      }
      return result
    })
    
    function getNavAttrs(isActive: boolean) {
      return isActive
        ? {
            disabled: true,
            role: 'presentation',
            'aria-hidden': true,
            'aria-disabled': true,
          }
        : { role: 'menuitem' }
    }
    
    function setPage(page: number) {
      if (page !== props.modelValue) {
        emit('update:modelValue', page)
      }
    }
    </script>
    
    <script lang="ts">
    export default {
      compatConfig: {
        MODE: 3,
      },
    }
    </script>