mirror of
				https://github.com/crater-invoice/crater.git
				synced 2025-10-28 12:11:08 -04:00 
			
		
		
		
	
		
			
				
	
	
		
			369 lines
		
	
	
		
			9.9 KiB
		
	
	
	
		
			Vue
		
	
	
	
	
	
			
		
		
	
	
			369 lines
		
	
	
		
			9.9 KiB
		
	
	
	
		
			Vue
		
	
	
	
	
	
| <template>
 | |
|   <div
 | |
|     v-if="shouldShowPagination"
 | |
|     class="
 | |
|       flex
 | |
|       items-center
 | |
|       justify-between
 | |
|       px-4
 | |
|       py-3
 | |
|       bg-white
 | |
|       border-t border-gray-200
 | |
|       sm:px-6
 | |
|       dark:bg-transparent dark:border-white/10
 | |
|     "
 | |
|   >
 | |
|     <div class="flex justify-between flex-1 sm:hidden">
 | |
|       <a
 | |
|         href="#"
 | |
|         :class="{
 | |
|           'disabled cursor-normal pointer-events-none !bg-gray-100 !text-gray-400 dark:!bg-gray-800 dark:!text-gray-500':
 | |
|             pagination.currentPage === 1,
 | |
|         }"
 | |
|         class="
 | |
|           relative
 | |
|           inline-flex
 | |
|           items-center
 | |
|           px-4
 | |
|           py-2
 | |
|           text-sm
 | |
|           font-medium
 | |
|           text-gray-700
 | |
|           bg-white
 | |
|           border border-gray-300
 | |
|           rounded-md
 | |
|           hover:bg-gray-50
 | |
|           dark:bg-gray-800 dark:border-gray-600 dark:text-gray-300
 | |
|         "
 | |
|         @click="pageClicked(pagination.currentPage - 1)"
 | |
|       >
 | |
|         Previous
 | |
|       </a>
 | |
|       <a
 | |
|         href="#"
 | |
|         :class="{
 | |
|           'disabled cursor-default pointer-events-none !bg-gray-100 !text-gray-400 dark:!bg-gray-800 dark:!text-gray-500':
 | |
|             pagination.currentPage === pagination.totalPages,
 | |
|         }"
 | |
|         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
 | |
|           rounded-md
 | |
|           hover:bg-gray-50
 | |
|           dark:bg-gray-800 dark:border-gray-600 dark:text-gray-300
 | |
|         "
 | |
|         @click="pageClicked(pagination.currentPage + 1)"
 | |
|       >
 | |
|         Next
 | |
|       </a>
 | |
|     </div>
 | |
|     <div class="hidden sm:flex-1 sm:flex sm:items-center sm:justify-between">
 | |
|       <div>
 | |
|         <p class="text-sm text-gray-700 dark:text-gray-400">
 | |
|           Showing
 | |
|           {{ ' ' }}
 | |
|           <span
 | |
|             v-if="pagination.limit && pagination.currentPage"
 | |
|             class="font-medium dark:text-gray-200"
 | |
|           >
 | |
|             {{
 | |
|               pagination.currentPage * pagination.limit - (pagination.limit - 1)
 | |
|             }}
 | |
|           </span>
 | |
|           {{ ' ' }}
 | |
|           to
 | |
|           {{ ' ' }}
 | |
|           <span
 | |
|             v-if="pagination.limit && pagination.currentPage"
 | |
|             class="font-medium dark:text-gray-200"
 | |
|           >
 | |
|             <span
 | |
|               v-if="
 | |
|                 pagination.currentPage * pagination.limit <=
 | |
|                 pagination.totalCount
 | |
|               "
 | |
|             >
 | |
|               {{ pagination.currentPage * pagination.limit }}
 | |
|             </span>
 | |
|             <span v-else>
 | |
|               {{ pagination.totalCount }}
 | |
|             </span>
 | |
|           </span>
 | |
|           {{ ' ' }}
 | |
|           of
 | |
|           {{ ' ' }}
 | |
|           <span v-if="pagination.totalCount" class="font-medium dark:text-gray-200">
 | |
|             {{ pagination.totalCount }}
 | |
|           </span>
 | |
|           {{ ' ' }}
 | |
|           results
 | |
|         </p>
 | |
|       </div>
 | |
|       <div>
 | |
|         <nav
 | |
|           class="relative z-0 inline-flex -space-x-px rounded-md shadow-sm"
 | |
|           aria-label="Pagination"
 | |
|         >
 | |
|           <a
 | |
|             href="#"
 | |
|             :class="{
 | |
|               'disabled cursor-normal pointer-events-none !bg-gray-100 !text-gray-400 dark:!bg-gray-800 dark:!text-gray-500':
 | |
|                 pagination.currentPage === 1,
 | |
|             }"
 | |
|             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
 | |
|               hover:bg-gray-50
 | |
|               dark:border-gray-600 dark:bg-gray-800 dark:text-gray-300
 | |
|             "
 | |
|             @click="pageClicked(pagination.currentPage - 1)"
 | |
|           >
 | |
|             <span class="sr-only">Previous</span>
 | |
|             <BaseIcon name="ChevronLeftIcon" />
 | |
|           </a>
 | |
|           <a
 | |
|             v-if="hasFirst"
 | |
|             href="#"
 | |
|             aria-current="page"
 | |
|             :class="{
 | |
|               'z-10 bg-primary-50 border-primary-500 text-primary-600 dark:bg-primary-500 dark:text-white':
 | |
|                 isActive(1),
 | |
|               'bg-white border-gray-300 text-gray-500 hover:bg-gray-50 dark:border-gray-600 dark:bg-gray-800 dark:text-gray-300':
 | |
|                 !isActive(1),
 | |
|             }"
 | |
|             class="
 | |
|               relative
 | |
|               inline-flex
 | |
|               items-center
 | |
|               px-4
 | |
|               py-2
 | |
|               text-sm
 | |
|               font-medium
 | |
|               border
 | |
|             "
 | |
|             @click="pageClicked(1)"
 | |
|           >
 | |
|             1
 | |
|           </a>
 | |
| 
 | |
|           <span
 | |
|             v-if="hasFirstEllipsis"
 | |
|             class="
 | |
|               relative
 | |
|               inline-flex
 | |
|               items-center
 | |
|               px-4
 | |
|               py-2
 | |
|               text-sm
 | |
|               font-medium
 | |
|               text-gray-700
 | |
|               bg-white
 | |
|               border border-gray-300
 | |
|               dark:border-gray-600 dark:bg-gray-800 dark:text-gray-300
 | |
|             "
 | |
|           >
 | |
|             ...
 | |
|           </span>
 | |
|           <a
 | |
|             v-for="page in pages"
 | |
|             :key="page"
 | |
|             href="#"
 | |
|             :class="{
 | |
|               'z-10 bg-primary-50 border-primary-500 text-primary-600 dark:bg-primary-500 dark:text-white':
 | |
|                 isActive(page),
 | |
|               'bg-white border-gray-300 text-gray-500 hover:bg-gray-50 dark:border-gray-600 dark:bg-gray-800 dark:text-gray-300':
 | |
|                 !isActive(page),
 | |
|               disabled: page === '...',
 | |
|             }"
 | |
|             class="
 | |
|               relative
 | |
|               items-center
 | |
|               hidden
 | |
|               px-4
 | |
|               py-2
 | |
|               text-sm
 | |
|               font-medium
 | |
|               text-gray-500
 | |
|               bg-white
 | |
|               border border-gray-300
 | |
|               hover:bg-gray-50
 | |
|               md:inline-flex
 | |
|             "
 | |
|             @click="pageClicked(page)"
 | |
|           >
 | |
|             {{ page }}
 | |
|           </a>
 | |
| 
 | |
|           <span
 | |
|             v-if="hasLastEllipsis"
 | |
|             class="
 | |
|               relative
 | |
|               inline-flex
 | |
|               items-center
 | |
|               px-4
 | |
|               py-2
 | |
|               text-sm
 | |
|               font-medium
 | |
|               text-gray-700
 | |
|               bg-white
 | |
|               border border-gray-300
 | |
|               dark:border-gray-600 dark:bg-gray-800 dark:text-gray-300
 | |
|             "
 | |
|           >
 | |
|             ...
 | |
|           </span>
 | |
|           <a
 | |
|             v-if="hasLast"
 | |
|             href="#"
 | |
|             aria-current="page"
 | |
|             :class="{
 | |
|               'z-10 bg-primary-50 border-primary-500 text-primary-600 dark:bg-primary-500 dark:text-white':
 | |
|                 isActive(pagination.totalPages),
 | |
|               'bg-white border-gray-300 text-gray-500 hover:bg-gray-50 dark:border-gray-600 dark:bg-gray-800 dark:text-gray-300':
 | |
|                 !isActive(pagination.totalPages),
 | |
|             }"
 | |
|             class="
 | |
|               relative
 | |
|               inline-flex
 | |
|               items-center
 | |
|               px-4
 | |
|               py-2
 | |
|               text-sm
 | |
|               font-medium
 | |
|               border
 | |
|             "
 | |
|             @click="pageClicked(pagination.totalPages)"
 | |
|           >
 | |
|             {{ pagination.totalPages }}
 | |
|           </a>
 | |
|           <a
 | |
|             href="#"
 | |
|             class="
 | |
|               relative
 | |
|               inline-flex
 | |
|               items-center
 | |
|               px-2
 | |
|               py-2
 | |
|               text-sm
 | |
|               font-medium
 | |
|               text-gray-500
 | |
|               bg-white
 | |
|               border border-gray-300
 | |
|               rounded-r-md
 | |
|               hover:bg-gray-50
 | |
|               dark:border-gray-600 dark:bg-gray-800 dark:text-gray-300
 | |
|             "
 | |
|             :class="{
 | |
|               'disabled cursor-default pointer-events-none !bg-gray-100 !text-gray-400 dark:!bg-gray-800 dark:!text-gray-500':
 | |
|                 pagination.currentPage === pagination.totalPages,
 | |
|             }"
 | |
|             @click="pageClicked(pagination.currentPage + 1)"
 | |
|           >
 | |
|             <span class="sr-only">Next</span>
 | |
|             <BaseIcon name="ChevronRightIcon" />
 | |
|           </a>
 | |
|         </nav>
 | |
|       </div>
 | |
|     </div>
 | |
|   </div>
 | |
| </template>
 | |
| 
 | |
| <script>
 | |
| // Todo: Need to convert this to Composition API
 | |
| 
 | |
| export default {
 | |
|   props: {
 | |
|     pagination: {
 | |
|       type: Object,
 | |
|       default: () => ({}),
 | |
|     },
 | |
|   },
 | |
|   computed: {
 | |
|     pages() {
 | |
|       return this.pagination.totalPages === undefined ? [] : this.pageLinks()
 | |
|     },
 | |
|     hasFirst() {
 | |
|       return this.pagination.currentPage >= 4 || this.pagination.totalPages < 10
 | |
|     },
 | |
|     hasLast() {
 | |
|       return (
 | |
|         this.pagination.currentPage <= this.pagination.totalPages - 3 ||
 | |
|         this.pagination.totalPages < 10
 | |
|       )
 | |
|     },
 | |
|     hasFirstEllipsis() {
 | |
|       return (
 | |
|         this.pagination.currentPage >= 4 && this.pagination.totalPages >= 10
 | |
|       )
 | |
|     },
 | |
|     hasLastEllipsis() {
 | |
|       return (
 | |
|         this.pagination.currentPage <= this.pagination.totalPages - 3 &&
 | |
|         this.pagination.totalPages >= 10
 | |
|       )
 | |
|     },
 | |
|     shouldShowPagination() {
 | |
|       if (this.pagination.totalPages === undefined) {
 | |
|         return false
 | |
|       }
 | |
|       if (this.pagination.count === 0) {
 | |
|         return false
 | |
|       }
 | |
|       return this.pagination.totalPages > 1
 | |
|     },
 | |
|   },
 | |
|   methods: {
 | |
|     isActive(page) {
 | |
|       const currentPage = this.pagination.currentPage || 1
 | |
|       return currentPage === page
 | |
|     },
 | |
|     pageClicked(page) {
 | |
|       if (
 | |
|         page === '...' ||
 | |
|         page === this.pagination.currentPage ||
 | |
|         page > this.pagination.totalPages ||
 | |
|         page < 1
 | |
|       ) {
 | |
|         return
 | |
|       }
 | |
| 
 | |
|       this.$emit('pageChange', page)
 | |
|     },
 | |
|     pageLinks() {
 | |
|       const pages = []
 | |
|       let left = 2
 | |
|       let right = this.pagination.totalPages - 1
 | |
|       if (this.pagination.totalPages >= 10) {
 | |
|         left = Math.max(1, this.pagination.currentPage - 2)
 | |
|         right = Math.min(
 | |
|           this.pagination.currentPage + 2,
 | |
|           this.pagination.totalPages
 | |
|         )
 | |
|       }
 | |
|       for (let i = left; i <= right; i++) {
 | |
|         pages.push(i)
 | |
|       }
 | |
|       return pages
 | |
|     },
 | |
|   },
 | |
| }
 | |
| </script>
 |