Added table pagination

This commit is contained in:
lucast committed 2026-10-06 02:52:26 +02:00
1 parent 440b6ef817
commit d9e1cb1c6f
1 file changed
+92 -5
+92 -5
View File
@@ -1,4 +1,5 @@
<script setup lang="ts"> <script setup lang="ts">
import { computed, ref, watch } from "vue"
import { GET_AUDIO_FILE, GET_FILE } from "@/utils/file" import { GET_AUDIO_FILE, GET_FILE } from "@/utils/file"
export type Row = { export type Row = {
@@ -12,9 +13,30 @@ export type Row = {
type TableProps = { type TableProps = {
rows: Row[] rows: Row[]
data: any data: any
pagination?: boolean
pageSize?: number
} }
const { rows, data } = defineProps<TableProps>() const props = withDefaults(defineProps<TableProps>(), {
pagination: false,
pageSize: 10,
})
const currentPage = ref(1)
const pageSize = computed(() => Math.max(1, Math.floor(props.pageSize)))
const pageCount = computed(() =>
Math.max(1, Math.ceil(props.data.length / pageSize.value)),
)
const visibleData = computed(() => {
if (!props.pagination) return props.data
const start = (currentPage.value - 1) * pageSize.value
return props.data.slice(start, start + pageSize.value)
})
watch(pageCount, (count) => {
if (currentPage.value > count) currentPage.value = count
})
</script> </script>
<template> <template>
@@ -22,7 +44,7 @@ const { rows, data } = defineProps<TableProps>()
<thead> <thead>
<tr> <tr>
<th <th
v-for="row in rows" v-for="row in props.rows"
:key="row.name" :key="row.name"
:class="row.class" :class="row.class"
:style="{ width: row.width }" :style="{ width: row.width }"
@@ -32,9 +54,16 @@ const { rows, data } = defineProps<TableProps>()
</tr> </tr>
</thead> </thead>
<tbody> <tbody>
<tr v-for="(item, i) in data" :key="i"> <tr
<td v-for="(row, j) in rows" :key="j"> v-for="(item, i) in visibleData"
<slot :name="row.name" :item="item" :index="i"> :key="(currentPage - 1) * pageSize + i"
>
<td v-for="(row, j) in props.rows" :key="j">
<slot
:name="row.name"
:item="item"
:index="props.pagination ? (currentPage - 1) * pageSize + i : i"
>
<nuxt-img <nuxt-img
v-if="row.type == 'image'" v-if="row.type == 'image'"
:src="GET_FILE(item[row.name])" :src="GET_FILE(item[row.name])"
@@ -50,6 +79,39 @@ const { rows, data } = defineProps<TableProps>()
</tr> </tr>
</tbody> </tbody>
</table> </table>
<nav
v-if="props.pagination && pageCount > 1"
class="pagination"
aria-label="Table pagination"
>
<button
type="button"
:disabled="currentPage === 1"
aria-label="Previous page"
@click="currentPage--"
>
Previous
</button>
<button
v-for="page in pageCount"
:key="page"
type="button"
:aria-label="`Page ${page}`"
:aria-current="currentPage === page ? 'page' : undefined"
:class="{ active: currentPage === page }"
@click="currentPage = page"
>
{{ page }}
</button>
<button
type="button"
:disabled="currentPage === pageCount"
aria-label="Next page"
@click="currentPage++"
>
Next
</button>
</nav>
</template> </template>
<style lang="scss" scoped> <style lang="scss" scoped>
@@ -85,4 +147,29 @@ const { rows, data } = defineProps<TableProps>()
padding: 5px 0; padding: 5px 0;
} }
} }
.pagination {
display: flex;
justify-content: center;
align-items: center;
gap: 0.4rem;
margin-top: 0.75rem;
& button {
padding: 0.4rem 0.65rem;
border-radius: $border-radius-standard;
background-color: $color-button;
cursor: pointer;
}
& button:hover:not(:disabled),
& button.active {
background-color: $color-button-hover;
}
& button:disabled {
cursor: default;
opacity: 0.5;
}
}
</style> </style>