PluginProbe ʕ •ᴥ•ʔ
Hostinger Reach – AI-Powered Email Marketing for WordPress / 1.7.0
Hostinger Reach – AI-Powered Email Marketing for WordPress v1.7.0
1.7.2 1.7.1 1.7.0 1.6.0 1.5.9 1.5.8 1.5.7 1.5.6 1.5.5 1.5.4 1.5.3 1.5.2 1.5.1 1.5.0 1.4.12 1.4.11 1.4.10 1.4.9 1.4.8 1.4.7 trunk 1.0.1 1.0.10 1.0.11 1.0.12 1.0.13 1.0.14 1.0.2 1.0.3 1.0.4 1.0.5 1.0.6 1.0.7 1.0.8 1.0.9 1.1.0 1.1.1 1.2.0 1.2.1 1.2.2 1.2.3 1.2.4 1.3.0 1.3.1 1.3.2 1.3.3 1.3.4 1.3.5 1.3.6 1.3.7 1.3.8 1.3.9 1.4.0 1.4.2 1.4.3 1.4.4 1.4.5 1.4.6
hostinger-reach / frontend / vue / components / Pagination.vue
hostinger-reach / frontend / vue / components Last commit date
Modals 1 month ago skeletons 5 months ago ActionButton.vue 5 months ago ActionButtonsSection.vue 5 months ago Banner.vue 3 months ago FAQ.vue 5 months ago FormItem.vue 2 months ago FormsSection.vue 11 months ago Hero.vue 1 month ago Integrations.vue 4 months ago Pagination.vue 10 months ago PluginEntriesTable.vue 4 months ago PluginEntry.vue 5 months ago PluginExpansion.vue 8 months ago SyncStatusLabel.vue 6 months ago Tabs.vue 9 months ago Toggle.vue 10 months ago UsageCard.vue 10 months ago UsageCardsRow.vue 1 year ago UsageCardsSection.vue 1 year ago WooCommerce.vue 5 months ago WooCommerceEntriesTable.vue 4 months ago
Pagination.vue
167 lines
1 <script setup lang="ts">
2 import { HButton, HIcon } from '@hostinger/hcomponents';
3 import { computed } from 'vue';
4
5 interface Props {
6 currentPage: number;
7 totalItems: number;
8 itemsPerPage: number;
9 totalVisible?: number;
10 disabled?: boolean;
11 }
12
13 const props = withDefaults(defineProps<Props>(), {
14 totalVisible: 5,
15 disabled: false
16 });
17
18 const emit = defineEmits<{
19 'page-change': [page: number];
20 'update:current-page': [page: number];
21 }>();
22
23 const totalPages = computed(() => Math.ceil(props.totalItems / props.itemsPerPage));
24
25 const visiblePages = computed(() => {
26 const pages: (number | string)[] = [];
27 const current = props.currentPage;
28 const total = totalPages.value;
29 const maxVisible = props.totalVisible;
30
31 if (total <= maxVisible) {
32 for (let i = 1; i <= total; i++) {
33 pages.push(i);
34 }
35
36 return pages;
37 }
38
39 pages.push(1);
40
41 const sidePages = Math.floor((maxVisible - 3) / 2);
42 const leftBound = Math.max(2, current - sidePages);
43 const rightBound = Math.min(total - 1, current + sidePages);
44
45 if (leftBound > 2) {
46 pages.push('...');
47 }
48
49 for (let i = leftBound; i <= rightBound; i++) {
50 if (i !== 1 && i !== total) {
51 pages.push(i);
52 }
53 }
54
55 if (rightBound < total - 1) {
56 pages.push('...');
57 }
58
59 if (total > 1) {
60 pages.push(total);
61 }
62
63 return pages;
64 });
65
66 const canGoPrevious = computed(() => props.currentPage > 1 && !props.disabled);
67 const canGoNext = computed(() => props.currentPage < totalPages.value && !props.disabled);
68
69 const goToPage = (page: number) => {
70 if (props.disabled || page < 1 || page > totalPages.value || page === props.currentPage) {
71 return;
72 }
73
74 emit('page-change', page);
75 emit('update:current-page', page);
76 };
77
78 const goToPrevious = () => {
79 if (canGoPrevious.value) {
80 goToPage(props.currentPage - 1);
81 }
82 };
83
84 const goToNext = () => {
85 if (canGoNext.value) {
86 goToPage(props.currentPage + 1);
87 }
88 };
89
90 const handleKeydown = (event: KeyboardEvent, page: number) => {
91 if (event.key === 'Enter' || event.key === ' ') {
92 event.preventDefault();
93 goToPage(page);
94 }
95 };
96 </script>
97
98 <template>
99 <nav
100 v-if="totalPages > 1"
101 class="pagination"
102 role="navigation"
103 :aria-label="`Pagination, page ${currentPage} of ${totalPages}`"
104 >
105 <HButton
106 variant="text"
107 color="neutral"
108 size="small"
109 :is-disabled="!canGoPrevious"
110 :aria-label="`Go to previous page, page ${currentPage - 1}`"
111 :tabindex="canGoPrevious ? 0 : -1"
112 @click="goToPrevious"
113 >
114 <HIcon name="ic-chevron-small-left-16" />
115 </HButton>
116
117 <template v-for="(page, index) in visiblePages" :key="`page-${page}-${index}`">
118 <span v-if="page === '...'" class="pagination-ellipsis" aria-hidden="true">...</span>
119 <HButton
120 v-else
121 variant="text"
122 size="small"
123 :color="page === currentPage ? 'primary' : 'neutral'"
124 :is-disabled="disabled"
125 :aria-label="page === currentPage ? `Current page, page ${page}` : `Go to page ${page}`"
126 :aria-current="page === currentPage ? 'page' : undefined"
127 :tabindex="disabled ? -1 : 0"
128 @click="goToPage(page as number)"
129 @keydown="handleKeydown($event, page as number)"
130 >
131 {{ page }}
132 </HButton>
133 </template>
134
135 <HButton
136 variant="text"
137 color="neutral"
138 size="small"
139 :is-disabled="!canGoNext"
140 :aria-label="`Go to next page, page ${currentPage + 1}`"
141 :tabindex="canGoNext ? 0 : -1"
142 @click="goToNext"
143 >
144 <HIcon name="ic-chevron-small-right-16" />
145 </HButton>
146 </nav>
147 </template>
148
149 <style scoped>
150 .pagination {
151 display: flex;
152 align-items: center;
153 gap: 8px;
154 }
155
156 .pagination-ellipsis {
157 display: flex;
158 align-items: center;
159 justify-content: center;
160 width: 48px;
161 height: 48px;
162 color: var(--neutral--700);
163 font-size: 14px;
164 font-weight: 500;
165 }
166 </style>
167