144 lines
4.6 KiB
Vue
144 lines
4.6 KiB
Vue
|
|
<script setup lang="ts">
|
||
|
|
import { computed } from 'vue';
|
||
|
|
import { router } from '@inertiajs/vue3';
|
||
|
|
|
||
|
|
import { Button } from '@/components/ui/button';
|
||
|
|
import {
|
||
|
|
Select,
|
||
|
|
SelectContent,
|
||
|
|
SelectItem,
|
||
|
|
SelectTrigger,
|
||
|
|
SelectValue,
|
||
|
|
} from '@/components/ui/select';
|
||
|
|
import { ChevronFirst, ChevronLast, ChevronLeft, ChevronRight } from 'lucide-vue-next';
|
||
|
|
|
||
|
|
interface PaginationData {
|
||
|
|
current_page: number;
|
||
|
|
per_page: number;
|
||
|
|
total: number;
|
||
|
|
last_page: number;
|
||
|
|
from: number;
|
||
|
|
to: number;
|
||
|
|
}
|
||
|
|
|
||
|
|
interface Props {
|
||
|
|
pagination: PaginationData;
|
||
|
|
selectedCount?: number;
|
||
|
|
perPageOptions?: number[];
|
||
|
|
}
|
||
|
|
|
||
|
|
const props = withDefaults(defineProps<Props>(), {
|
||
|
|
selectedCount: 0,
|
||
|
|
perPageOptions: () => [10, 25, 50, 100],
|
||
|
|
});
|
||
|
|
|
||
|
|
const canGoPrevious = computed(() => props.pagination.current_page > 1);
|
||
|
|
const canGoNext = computed(() => props.pagination.current_page < props.pagination.last_page);
|
||
|
|
|
||
|
|
const handlePerPageChange = (value: unknown): void => {
|
||
|
|
const str = value != null ? String(value) : null;
|
||
|
|
if (str == null) return;
|
||
|
|
const perPage = Number.parseInt(str, 10);
|
||
|
|
const url = new URL(window.location.href);
|
||
|
|
url.searchParams.set('per_page', perPage.toString());
|
||
|
|
url.searchParams.set('page', '1');
|
||
|
|
|
||
|
|
router.get(url.pathname + url.search, {}, {
|
||
|
|
preserveState: true,
|
||
|
|
preserveScroll: true,
|
||
|
|
});
|
||
|
|
};
|
||
|
|
|
||
|
|
const goToPage = (page: number): void => {
|
||
|
|
if (page < 1 || page > props.pagination.last_page) {
|
||
|
|
return;
|
||
|
|
}
|
||
|
|
|
||
|
|
const url = new URL(window.location.href);
|
||
|
|
url.searchParams.set('page', page.toString());
|
||
|
|
|
||
|
|
router.get(url.pathname + url.search, {}, {
|
||
|
|
preserveState: true,
|
||
|
|
preserveScroll: true,
|
||
|
|
});
|
||
|
|
};
|
||
|
|
</script>
|
||
|
|
|
||
|
|
<template>
|
||
|
|
<div class="flex flex-col gap-4 px-4 sm:flex-row sm:items-center sm:justify-between">
|
||
|
|
<div class="text-muted-foreground text-sm">
|
||
|
|
<span v-if="selectedCount > 0">
|
||
|
|
{{ selectedCount }} sur {{ pagination.total }} ligne(s) sélectionnée(s).
|
||
|
|
</span>
|
||
|
|
<span v-else>
|
||
|
|
{{ pagination.total }} ligne(s) au total
|
||
|
|
</span>
|
||
|
|
</div>
|
||
|
|
|
||
|
|
<div class="flex flex-col gap-4 sm:flex-row sm:items-center sm:gap-6">
|
||
|
|
<div class="flex items-center gap-2">
|
||
|
|
<span class="text-muted-foreground hidden text-sm sm:inline">Lignes par page</span>
|
||
|
|
<Select
|
||
|
|
:model-value="pagination.per_page.toString()"
|
||
|
|
@update:model-value="handlePerPageChange"
|
||
|
|
>
|
||
|
|
<SelectTrigger class="h-8 w-[70px]">
|
||
|
|
<SelectValue />
|
||
|
|
</SelectTrigger>
|
||
|
|
<SelectContent>
|
||
|
|
<SelectItem
|
||
|
|
v-for="option in perPageOptions"
|
||
|
|
:key="option"
|
||
|
|
:value="option.toString()"
|
||
|
|
>
|
||
|
|
{{ option }}
|
||
|
|
</SelectItem>
|
||
|
|
</SelectContent>
|
||
|
|
</Select>
|
||
|
|
</div>
|
||
|
|
|
||
|
|
<div class="text-muted-foreground hidden text-sm md:inline">
|
||
|
|
Page {{ pagination.current_page }} sur {{ pagination.last_page }}
|
||
|
|
</div>
|
||
|
|
|
||
|
|
<div class="flex items-center justify-center gap-2">
|
||
|
|
<Button
|
||
|
|
variant="outline"
|
||
|
|
size="sm"
|
||
|
|
:disabled="!canGoPrevious"
|
||
|
|
@click="goToPage(1)"
|
||
|
|
>
|
||
|
|
<ChevronFirst class="size-4" />
|
||
|
|
</Button>
|
||
|
|
<Button
|
||
|
|
variant="outline"
|
||
|
|
size="sm"
|
||
|
|
:disabled="!canGoPrevious"
|
||
|
|
@click="goToPage(pagination.current_page - 1)"
|
||
|
|
>
|
||
|
|
<ChevronLeft class="size-4" />
|
||
|
|
</Button>
|
||
|
|
<div class="text-muted-foreground mx-2 text-sm md:hidden">
|
||
|
|
{{ pagination.current_page }}/{{ pagination.last_page }}
|
||
|
|
</div>
|
||
|
|
<Button
|
||
|
|
variant="outline"
|
||
|
|
size="sm"
|
||
|
|
:disabled="!canGoNext"
|
||
|
|
@click="goToPage(pagination.current_page + 1)"
|
||
|
|
>
|
||
|
|
<ChevronRight class="size-4" />
|
||
|
|
</Button>
|
||
|
|
<Button
|
||
|
|
variant="outline"
|
||
|
|
size="sm"
|
||
|
|
:disabled="!canGoNext"
|
||
|
|
@click="goToPage(pagination.last_page)"
|
||
|
|
>
|
||
|
|
<ChevronLast class="size-4" />
|
||
|
|
</Button>
|
||
|
|
</div>
|
||
|
|
</div>
|
||
|
|
</div>
|
||
|
|
</template>
|