Created
September 4, 2022 21:57
-
-
Save joelbutcher/d7dd935b5dfdf9ecef08bb3d1e72a014 to your computer and use it in GitHub Desktop.
A Vue.js implemetation of the Laravel Paginator Links for Blade
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
<script setup> | |
import {computed} from 'vue'; | |
defineEmits(['fetchData']); | |
const props = defineProps({ | |
paginator: { | |
type: Object, | |
required: true, | |
default: { | |
current_page: 1, | |
data: [], | |
first_page_url: ',', | |
from: 1, | |
last_page: 1, | |
last_page_url: '', | |
links: [], | |
next_page_url: ',', | |
path: '', | |
per_page: 15, | |
prev_page_url: null, | |
to: 15, | |
total: 100, | |
}, | |
}, | |
}); | |
const count = computed(() => props.paginator.data.length); | |
const firstItem = computed(() => count > 0 ? (props.paginator.current_page - 1) * props.paginator.per_page + 1 : null); | |
const lastItem = computed(() => count > 0 ? firstItem + count - 1 : null); | |
const nextPage = computed(() => props.paginator.current_page + 1); | |
const onFirstPage = computed(() => props.paginator.current_page <= 1); | |
const hasMorePages = computed(() => props.paginator.current_page < props.paginator.last_page); | |
const paginationLinks = computed(() => props.paginator?.links?.filter((link) => !['« Previous', 'Next »'].includes(link.label))); | |
</script> | |
<template> | |
<nav role="navigation" aria-label="Pagination Navigation" class="flex items-center justify-between"> | |
<div class="flex justify-between flex-1 sm:hidden"> | |
<template v-if="onFirstPage"> | |
<span class="relative inline-flex items-center px-4 py-2 text-sm font-medium text-gray-500 bg-white border border-gray-300 cursor-default leading-5 rounded-md"> | |
« Previous | |
</span> | |
</template> | |
<template v-else> | |
<button | |
@click="$emit('fetchData', paginator.last_page)" | |
:href="paginator.last_page_url" | |
class="relative inline-flex items-center px-4 py-2 text-sm font-medium text-gray-700 bg-white border border-gray-300 leading-5 rounded-md hover:text-gray-500 focus:outline-none focus:ring ring-gray-300 focus:border-blue-300 active:bg-gray-100 active:text-gray-700 transition ease-in-out duration-150" | |
> | |
« Previous | |
</button> | |
</template> | |
<template v-if="hasMorePages"> | |
<button | |
@click="$emit('fetchPage', nextPage)" | |
class="relative inline-flex items-center px-4 py-2 ml-3 text-sm font-medium text-gray-700 bg-white border border-gray-300 leading-5 rounded-md hover:text-gray-500 focus:outline-none focus:ring ring-gray-300 focus:border-blue-300 active:bg-gray-100 active:text-gray-700 transition ease-in-out duration-150" | |
> | |
Next » | |
</button> | |
</template> | |
<template v-else> | |
<span class="relative inline-flex items-center px-4 py-2 ml-3 text-sm font-medium text-gray-500 bg-white border border-gray-300 cursor-default leading-5 rounded-md"> | |
Next » | |
</span> | |
</template> | |
</div> | |
<div class="hidden sm:flex-1 sm:flex sm:items-center sm:justify-between"> | |
<div> | |
<p class="text-sm text-gray-700 leading-5"> | |
Showing | |
<template v-if="firstItem"> | |
<span class="font-medium">{{ firstItem }}</span> | |
to | |
<span class="font-medium">{{ lastItem }}</span> | |
</template> | |
<template v-else> | |
{{ count }} | |
</template> | |
of | |
<span class="font-medium"> | |
{{ paginator.total }} | |
</span> | |
results | |
</p> | |
</div> | |
<div> | |
<span class="relative z-0 inline-flex shadow-sm rounded-md"> | |
<!-- Previous Page Link --> | |
<template v-if="onFirstPage"> | |
<span aria-disabled="true" aria-label="« Previous"> | |
<span | |
class="relative inline-flex items-center px-2 py-2 text-sm font-medium text-gray-500 bg-white border border-gray-300 cursor-default rounded-l-md leading-5" | |
aria-hidden="true"> | |
<svg class="w-5 h-5" fill="currentColor" viewBox="0 0 20 20"> | |
<path fill-rule="evenodd" | |
d="M12.707 5.293a1 1 0 010 1.414L9.414 10l3.293 3.293a1 1 0 01-1.414 1.414l-4-4a1 1 0 010-1.414l4-4a1 1 0 011.414 0z" | |
clip-rule="evenodd"/> | |
</svg> | |
</span> | |
</span> | |
</template> | |
<template v-else> | |
<button | |
@click="$emit('fetchData', paginator.last_page)" | |
class="relative inline-flex items-center px-2 py-2 text-sm font-medium text-gray-500 bg-white border border-gray-300 rounded-l-md leading-5 hover:text-gray-400 focus:z-10 focus:outline-none focus:ring ring-gray-300 focus:border-blue-300 active:bg-gray-100 active:text-gray-500 transition ease-in-out duration-150" | |
aria-label="« Previous"> | |
<svg class="w-5 h-5" fill="currentColor" viewBox="0 0 20 20"> | |
<path fill-rule="evenodd" | |
d="M12.707 5.293a1 1 0 010 1.414L9.414 10l3.293 3.293a1 1 0 01-1.414 1.414l-4-4a1 1 0 010-1.414l4-4a1 1 0 011.414 0z" | |
clip-rule="evenodd"/> | |
</svg> | |
</button> | |
</template> | |
<!-- Pagination Elements --> | |
<template v-for="link in paginationLinks"> | |
<!-- "Three Dots" Separator --> | |
<template v-if="link.label === '...'"> | |
<span aria-disabled="true"> | |
<span class="relative inline-flex items-center px-4 py-2 -ml-px text-sm font-medium text-gray-700 bg-white border border-gray-300 cursor-default leading-5"> | |
{{ link.label }} | |
</span> | |
</span> | |
</template> | |
<!-- Numerical Links --> | |
<template v-else> | |
<template v-if="link.active"> | |
<span aria-current="page"> | |
<span class="relative inline-flex items-center px-4 py-2 -ml-px text-sm font-medium text-gray-500 bg-white border border-gray-300 cursor-default leading-5"> | |
{{ parseInt(link.label) }} | |
</span> | |
</span> | |
</template> | |
<template v-else> | |
<button | |
@click="$emit('fetchData', parseInt(link.label))" | |
rel="prev" | |
:aria-label="`Go to page ${parseInt(link.label)}`" | |
class="relative inline-flex items-center px-4 py-2 -ml-px text-sm font-medium text-gray-700 bg-white border border-gray-300 leading-5 hover:text-gray-500 focus:z-10 focus:outline-none focus:ring ring-gray-300 focus:border-blue-300 active:bg-gray-100 active:text-gray-700 transition ease-in-out duration-150" | |
> | |
{{ parseInt(link.label) }} | |
</button> | |
</template> | |
</template> | |
</template> | |
<!-- Next Page Link --> | |
<template v-if="hasMorePages"> | |
<button | |
@click="$emit('fetchData', nextPage)" | |
rel="next" | |
class="relative inline-flex items-center px-2 py-2 -ml-px text-sm font-medium text-gray-500 bg-white border border-gray-300 rounded-r-md leading-5 hover:text-gray-400 focus:z-10 focus:outline-none focus:ring ring-gray-300 focus:border-blue-300 active:bg-gray-100 active:text-gray-500 transition ease-in-out duration-150" | |
aria-label="Next »"> | |
<svg class="w-5 h-5" fill="currentColor" viewBox="0 0 20 20"> | |
<path fill-rule="evenodd" d="M7.293 14.707a1 1 0 010-1.414L10.586 10 7.293 6.707a1 1 0 011.414-1.414l4 4a1 1 0 010 1.414l-4 4a1 1 0 01-1.414 0z" clip-rule="evenodd" /> | |
</svg> | |
</button> | |
</template> | |
<template v-else> | |
<span aria-disabled="true" aria-label="Next »"> | |
<span class="relative inline-flex items-center px-2 py-2 -ml-px text-sm font-medium text-gray-500 bg-white border border-gray-300 cursor-default rounded-r-md leading-5" aria-hidden="true"> | |
<svg class="w-5 h-5" fill="currentColor" viewBox="0 0 20 20"> | |
<path fill-rule="evenodd" d="M7.293 14.707a1 1 0 010-1.414L10.586 10 7.293 6.707a1 1 0 011.414-1.414l4 4a1 1 0 010 1.414l-4 4a1 1 0 01-1.414 0z" clip-rule="evenodd" /> | |
</svg> | |
</span> | |
</span> | |
</template> | |
</span> | |
</div> | |
</div> | |
</nav> | |
</template> |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment