Merge pull request #16 from nurRiyad/pagination
Add pagination feature for blog post
This commit is contained in:
23
components/blog/Loader.vue
Normal file
23
components/blog/Loader.vue
Normal file
@@ -0,0 +1,23 @@
|
||||
<script>
|
||||
import { ContentLoader } from 'vue-content-loader'
|
||||
|
||||
export default {
|
||||
components: { ContentLoader },
|
||||
}
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<ContentLoader
|
||||
viewBox="0 0 400 160"
|
||||
:speed="2"
|
||||
primary-color="#c9c9c9"
|
||||
secondary-color="#ecebeb"
|
||||
>
|
||||
<rect x="10" y="13" rx="0" ry="0" width="95" height="86" />
|
||||
<rect x="119" y="19" rx="0" ry="0" width="219" height="16" />
|
||||
<rect x="119" y="49" rx="0" ry="0" width="277" height="16" />
|
||||
<rect x="119" y="82" rx="0" ry="0" width="56" height="14" />
|
||||
<rect x="195" y="82" rx="0" ry="0" width="56" height="14" />
|
||||
<rect x="272" y="82" rx="0" ry="0" width="56" height="14" />
|
||||
</ContentLoader>
|
||||
</template>
|
||||
@@ -11,6 +11,7 @@
|
||||
},
|
||||
"devDependencies": {
|
||||
"@antfu/eslint-config": "^0.38.5",
|
||||
"@formkit/auto-animate": "^0.7.0",
|
||||
"@nuxt/content": "^2.7.2",
|
||||
"@nuxt/image-edge": "^1.0.0-27840416.dc1ed65",
|
||||
"@nuxtjs/fontaine": "^0.2.5",
|
||||
@@ -21,6 +22,7 @@
|
||||
"nuxt": "^3.6.5",
|
||||
"nuxt-icon": "^0.4.0",
|
||||
"nuxt-simple-sitemap": "^3.1.3",
|
||||
"typescript": "^5.0.4"
|
||||
"typescript": "^5.0.4",
|
||||
"vue-content-loader": "^2.0.1"
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,6 +1,9 @@
|
||||
<script lang="ts" setup>
|
||||
const { data } = await useAsyncData('home', () => queryContent('/blogs').sort({ _id: -1 }).find())
|
||||
|
||||
const elementPerPgae = ref(4)
|
||||
const pageNumber = ref(1)
|
||||
|
||||
const formatedData = computed(() => {
|
||||
return data.value?.map((articles) => {
|
||||
return {
|
||||
@@ -14,9 +17,36 @@ const formatedData = computed(() => {
|
||||
tags: articles.tags || [],
|
||||
published: articles.published || false,
|
||||
}
|
||||
}) || []
|
||||
})
|
||||
|
||||
const paginatedData = computed(() => {
|
||||
return formatedData.value.filter((data, idx) => {
|
||||
const startInd = ((pageNumber.value - 1) * elementPerPgae.value)
|
||||
const endInd = (pageNumber.value * elementPerPgae.value) - 1
|
||||
|
||||
if (idx >= startInd && idx <= endInd)
|
||||
return true
|
||||
else return false
|
||||
}) || []
|
||||
})
|
||||
|
||||
function onPreviousPageClick() {
|
||||
if (pageNumber.value > 1)
|
||||
pageNumber.value -= 1
|
||||
}
|
||||
|
||||
const totalPage = computed(() => {
|
||||
const ttlContent = formatedData.value.length || 0
|
||||
const totalPage = Math.ceil(ttlContent / elementPerPgae.value)
|
||||
return totalPage
|
||||
})
|
||||
|
||||
function onNextPageClick() {
|
||||
if (pageNumber.value < totalPage.value)
|
||||
pageNumber.value += 1
|
||||
}
|
||||
|
||||
useHead({
|
||||
title: 'Archive',
|
||||
meta: [
|
||||
@@ -32,8 +62,9 @@ useHead({
|
||||
<template>
|
||||
<main class="container max-w-5xl mx-auto text-zinc-600">
|
||||
<ArchiveHero />
|
||||
<div class="space-y-5 my-5">
|
||||
<template v-for="post in formatedData" :key="post.title">
|
||||
<ClientOnly>
|
||||
<div v-auto-animate class="space-y-5 my-5">
|
||||
<template v-for="post in paginatedData" :key="post.title">
|
||||
<ArchiveCard
|
||||
:path="post.path"
|
||||
:title="post.title"
|
||||
@@ -47,5 +78,22 @@ useHead({
|
||||
/>
|
||||
</template>
|
||||
</div>
|
||||
|
||||
<template #fallback>
|
||||
<!-- this will be rendered on server side -->
|
||||
<BlogLoader />
|
||||
<BlogLoader />
|
||||
</template>
|
||||
</ClientOnly>
|
||||
|
||||
<div class="flex justify-center items-center space-x-6">
|
||||
<button :disabled="pageNumber <= 1" @click="onPreviousPageClick">
|
||||
<Icon name="mdi:code-less-than" size="30" :class="{ 'text-sky-700': pageNumber > 1 }" />
|
||||
</button>
|
||||
<p>{{ pageNumber }} / {{ totalPage }}</p>
|
||||
<button :disabled="pageNumber >= totalPage" @click="onNextPageClick">
|
||||
<Icon name="mdi:code-greater-than" size="30" :class="{ 'text-sky-700': pageNumber < totalPage }" />
|
||||
</button>
|
||||
</div>
|
||||
</main>
|
||||
</template>
|
||||
|
||||
6
plugins/formik.client.ts
Normal file
6
plugins/formik.client.ts
Normal file
@@ -0,0 +1,6 @@
|
||||
import { autoAnimatePlugin } from '@formkit/auto-animate/vue'
|
||||
|
||||
export default defineNuxtPlugin((nuxtApp) => {
|
||||
// Doing something with nuxtApp
|
||||
nuxtApp.vueApp.use(autoAnimatePlugin)
|
||||
})
|
||||
10
yarn.lock
10
yarn.lock
@@ -642,6 +642,11 @@
|
||||
resolved "https://registry.yarnpkg.com/@fastify/accept-negotiator/-/accept-negotiator-1.1.0.tgz#c1c66b3b771c09742a54dd5bc87c582f6b0630ff"
|
||||
integrity sha512-OIHZrb2ImZ7XG85HXOONLcJWGosv7sIvM2ifAPQVhg9Lv7qdmMBNVaai4QTdyuaqbKM5eO6sLSQOYI7wEQeCJQ==
|
||||
|
||||
"@formkit/auto-animate@^0.7.0":
|
||||
version "0.7.0"
|
||||
resolved "https://registry.yarnpkg.com/@formkit/auto-animate/-/auto-animate-0.7.0.tgz#7a68df578e972d63d999378571ce6e012d9059ae"
|
||||
integrity sha512-RczHUr0AhRPssREoNdRjLfk2b/id9/DFnbIq18QM8L7E4zNV3XH+WO480EZ46BQHDEsv76YPJ0JbG2Y2i3GfXw==
|
||||
|
||||
"@humanwhocodes/config-array@^0.11.10":
|
||||
version "0.11.10"
|
||||
resolved "https://registry.yarnpkg.com/@humanwhocodes/config-array/-/config-array-0.11.10.tgz#5a3ffe32cc9306365fb3fd572596cd602d5e12d2"
|
||||
@@ -8330,6 +8335,11 @@ vue-bundle-renderer@^1.0.3:
|
||||
dependencies:
|
||||
ufo "^1.1.1"
|
||||
|
||||
vue-content-loader@^2.0.1:
|
||||
version "2.0.1"
|
||||
resolved "https://registry.yarnpkg.com/vue-content-loader/-/vue-content-loader-2.0.1.tgz#c6a3ff0e653671e5e8cff9e0c3814e6d04d3411d"
|
||||
integrity sha512-pkof4+q2xmzNEdhqelxtJejeP/vQUJtLle4/v2ueG+HURqM9Q/GIGC8GJ2bVVWeLfTDET51jqimwQdmxJTlu0g==
|
||||
|
||||
vue-devtools-stub@^0.1.0:
|
||||
version "0.1.0"
|
||||
resolved "https://registry.yarnpkg.com/vue-devtools-stub/-/vue-devtools-stub-0.1.0.tgz#a65b9485edecd4273cedcb8102c739b83add2c81"
|
||||
|
||||
Reference in New Issue
Block a user