Add pagination feature for blog post
Signed-off-by: Al Asad Nur Riyad <alasadnurriyad@Als-MacBook-Pro.local>
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": {
|
"devDependencies": {
|
||||||
"@antfu/eslint-config": "^0.38.5",
|
"@antfu/eslint-config": "^0.38.5",
|
||||||
|
"@formkit/auto-animate": "^0.7.0",
|
||||||
"@nuxt/content": "^2.7.2",
|
"@nuxt/content": "^2.7.2",
|
||||||
"@nuxt/image-edge": "^1.0.0-27840416.dc1ed65",
|
"@nuxt/image-edge": "^1.0.0-27840416.dc1ed65",
|
||||||
"@nuxtjs/fontaine": "^0.2.5",
|
"@nuxtjs/fontaine": "^0.2.5",
|
||||||
@@ -21,6 +22,7 @@
|
|||||||
"nuxt": "^3.6.5",
|
"nuxt": "^3.6.5",
|
||||||
"nuxt-icon": "^0.4.0",
|
"nuxt-icon": "^0.4.0",
|
||||||
"nuxt-simple-sitemap": "^3.1.3",
|
"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>
|
<script lang="ts" setup>
|
||||||
const { data } = await useAsyncData('home', () => queryContent('/blogs').sort({ _id: -1 }).find())
|
const { data } = await useAsyncData('home', () => queryContent('/blogs').sort({ _id: -1 }).find())
|
||||||
|
|
||||||
|
const elementPerPgae = ref(4)
|
||||||
|
const pageNumber = ref(1)
|
||||||
|
|
||||||
const formatedData = computed(() => {
|
const formatedData = computed(() => {
|
||||||
return data.value?.map((articles) => {
|
return data.value?.map((articles) => {
|
||||||
return {
|
return {
|
||||||
@@ -14,9 +17,36 @@ const formatedData = computed(() => {
|
|||||||
tags: articles.tags || [],
|
tags: articles.tags || [],
|
||||||
published: articles.published || false,
|
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 isNextpageAvailable = computed(() => {
|
||||||
|
if (pageNumber.value * elementPerPgae.value <= formatedData.value.length)
|
||||||
|
return true
|
||||||
|
else return false
|
||||||
|
})
|
||||||
|
|
||||||
|
function onNextPageClick() {
|
||||||
|
if (isNextpageAvailable.value)
|
||||||
|
pageNumber.value += 1
|
||||||
|
}
|
||||||
|
|
||||||
useHead({
|
useHead({
|
||||||
title: 'Archive',
|
title: 'Archive',
|
||||||
meta: [
|
meta: [
|
||||||
@@ -32,8 +62,9 @@ useHead({
|
|||||||
<template>
|
<template>
|
||||||
<main class="container max-w-5xl mx-auto text-zinc-600">
|
<main class="container max-w-5xl mx-auto text-zinc-600">
|
||||||
<ArchiveHero />
|
<ArchiveHero />
|
||||||
<div class="space-y-5 my-5">
|
<ClientOnly>
|
||||||
<template v-for="post in formatedData" :key="post.title">
|
<div v-auto-animate class="space-y-5 my-5">
|
||||||
|
<template v-for="post in paginatedData" :key="post.title">
|
||||||
<ArchiveCard
|
<ArchiveCard
|
||||||
:path="post.path"
|
:path="post.path"
|
||||||
:title="post.title"
|
:title="post.title"
|
||||||
@@ -47,5 +78,22 @@ useHead({
|
|||||||
/>
|
/>
|
||||||
</template>
|
</template>
|
||||||
</div>
|
</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 }}</p>
|
||||||
|
<button :disabled="!isNextpageAvailable" @click="onNextPageClick">
|
||||||
|
<Icon name="mdi:code-greater-than" size="30" :class="{ 'text-sky-700': isNextpageAvailable }" />
|
||||||
|
</button>
|
||||||
|
</div>
|
||||||
</main>
|
</main>
|
||||||
</template>
|
</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"
|
resolved "https://registry.yarnpkg.com/@fastify/accept-negotiator/-/accept-negotiator-1.1.0.tgz#c1c66b3b771c09742a54dd5bc87c582f6b0630ff"
|
||||||
integrity sha512-OIHZrb2ImZ7XG85HXOONLcJWGosv7sIvM2ifAPQVhg9Lv7qdmMBNVaai4QTdyuaqbKM5eO6sLSQOYI7wEQeCJQ==
|
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":
|
"@humanwhocodes/config-array@^0.11.10":
|
||||||
version "0.11.10"
|
version "0.11.10"
|
||||||
resolved "https://registry.yarnpkg.com/@humanwhocodes/config-array/-/config-array-0.11.10.tgz#5a3ffe32cc9306365fb3fd572596cd602d5e12d2"
|
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:
|
dependencies:
|
||||||
ufo "^1.1.1"
|
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:
|
vue-devtools-stub@^0.1.0:
|
||||||
version "0.1.0"
|
version "0.1.0"
|
||||||
resolved "https://registry.yarnpkg.com/vue-devtools-stub/-/vue-devtools-stub-0.1.0.tgz#a65b9485edecd4273cedcb8102c739b83add2c81"
|
resolved "https://registry.yarnpkg.com/vue-devtools-stub/-/vue-devtools-stub-0.1.0.tgz#a65b9485edecd4273cedcb8102c739b83add2c81"
|
||||||
|
|||||||
Reference in New Issue
Block a user