Skip to content

Commit

Permalink
Feat advanced pagination (#61)
Browse files Browse the repository at this point in the history
Co-authored-by: Robin Kaggl <[email protected]>
  • Loading branch information
kaggl and Robin Kaggl authored Feb 20, 2024
1 parent 1cb2e87 commit f90f411
Show file tree
Hide file tree
Showing 9 changed files with 339 additions and 91 deletions.
86 changes: 78 additions & 8 deletions components/detail-disclosure.vue
Original file line number Diff line number Diff line change
Expand Up @@ -3,21 +3,26 @@ import { get } from "lodash-es";
import { ChevronRight } from "lucide-vue-next";
import GenericDisclosure from "@/components/generic-disclosure.vue";
import type { AnyEntity } from "@/types/schema";
import { NuxtLink } from "#components";
defineProps<{
rels: Array<object>;
const props = defineProps<{
rels: Array<AnyEntity>;
gridClass?: string;
title: string;
defaultOpen?: boolean;
headers?: Array<string>;
headers?: Array<string | [string, string]>;
customSlot?: boolean;
linkTo?: boolean;
collectionName: string;
}>();
const locale = useLocale();
const t = useTranslations();
const page = ref(0);
const limit = 25;
const all = props.rels.length;
</script>

<template>
Expand All @@ -28,12 +33,70 @@ const t = useTranslations();
>
<slot>
<div class="p-2">
<div v-if="all > limit" class="mb-4 flex justify-between">
<button
v-if="page > 1"
class="rounded border p-1 transition hover:bg-slate-300 active:bg-slate-400"
@click="page--"
>
<ChevronRight class="h-5 w-5 rotate-180" />
</button>
<div v-else class="flex items-center rounded border p-1 text-gray-300">
<ChevronRight class="h-5 w-5 rotate-180" />
</div>
<div>
<div class="flex flex-wrap justify-center gap-1">
<button v-if="page > 2" class="underline" @click="page = 0">1</button>

<span v-if="page > 3">...</span>
<template v-for="n in 5" :key="n - 2 + page">
<button
v-if="n - 2 + page >= 1 && n - 2 + page <= Math.ceil(all / limit)"
:class="n === 3 ? 'font-semibold' : 'underline'"
@click="page = n - 3 + page"
>
{{ n - 2 + page }}
</button>
</template>
<span v-if="page + 4 < Math.ceil(all / limit)">...</span>
<button
v-if="page + 3 < Math.ceil(all / limit)"
class="underline"
@click="page = Math.ceil(all / limit) - 1"
>
{{ Math.ceil(all / limit) }}
</button>
</div>
<div class="text-center">
{{ page * limit + 1 }} - {{ Math.min((page + 1) * limit, all) }} / {{ all }}
</div>
</div>
<button
v-if="page < Math.ceil(all / limit) - 1"
class="rounded border p-1 transition hover:bg-slate-300 active:bg-slate-400"
@click="page++"
>
<ChevronRight class="h-5 w-5" />
</button>
<div v-else class="flex items-center rounded border p-1 text-gray-300">
<ChevronRight class="h-5 w-5" />
</div>
</div>
<div class="grid gap-2" :class="linkTo ? 'mr-6 ' + gridClass : gridClass">
<span v-for="header in headers" :key="header" class="font-bold">
{{ t(`collection-keys.${collectionName}["${header}"]`) }}
</span>
<template v-for="header in headers" :key="header">
<span v-if="typeof header === 'string'">
{{ t(`collection-keys.${collectionName}["${header}"]`) }}
</span>
<span v-else class="font-bold">
{{ t(`collection-keys.${collectionName}["${header[0]}"]`) }}
</span>
</template>
</div>
<div v-for="hit in rels" :key="String(hit)" class="mt-1 border-t pt-1">
<div
v-for="hit in rels.slice(page * limit, (page + 1) * limit)"
:key="String(hit)"
class="mt-1 border-t pt-1"
>
<component
:is="linkTo ? NuxtLink : 'div'"
:to="`/${locale}/detail/${hit?.target?.model.toLowerCase() + 's'}/${
Expand All @@ -43,7 +106,14 @@ const t = useTranslations();
:class="linkTo && 'rounded transition hover:bg-slate-200 active:bg-slate-300 p-1 -ml-1'"
>
<div class="grid items-center gap-2" :class="gridClass">
<span v-for="header in headers" :key="hit + header">{{ get(hit, header) }}</span>
<template v-for="header in headers" :key="hit + header">
<span v-if="typeof header === 'string'">
{{ get(hit, header) }}
</span>
<span v-else>
{{ get(hit, header[0]) || get(hit, header[1]) }}
</span>
</template>
</div>
<ChevronRight v-if="linkTo" class="h-5 w-5 shrink-0" />
</component>
Expand Down
2 changes: 1 addition & 1 deletion components/generic-disclosure.vue
Original file line number Diff line number Diff line change
Expand Up @@ -17,7 +17,7 @@ defineProps<{
:disabled="disabled"
:class="
disabled
? 'opacity-50 cursor-not-allowed'
? 'opacity-50'
: 'hover:border-primary-400 hover:bg-primary-300 border-primary-300 bg-primary-200 shadow-sm'
"
>
Expand Down
148 changes: 100 additions & 48 deletions components/pagination.vue
Original file line number Diff line number Diff line change
@@ -1,8 +1,7 @@
<script setup lang="ts">
import { ChevronUp } from "lucide-vue-next";
import type { RouteLocationNormalized } from "vue-router";
const route: RouteLocationNormalized = useRoute();
const route = useRoute();
defineProps<{
page: number;
Expand All @@ -14,59 +13,112 @@ const t = useTranslations();
</script>

<template>
<div class="flex items-center justify-between">
<NuxtLink
v-if="page > 1"
data-testid="prevPage"
:to="{
query: {
...route.query,
page: page - 1,
},
}"
>
<div
class="cursor-pointer rounded border p-2 transition hover:bg-slate-200 active:bg-slate-300"
<div class="grid grid-cols-[1fr_auto_1fr] items-center gap-2">
<div class="flex items-center gap-1">
<NuxtLink
v-if="page > 1"
data-testid="prevPage"
:to="{
query: {
...route.query,
page: page - 1,
},
}"
>
<div
class="w-fit cursor-pointer rounded border p-2 transition hover:bg-slate-200 active:bg-slate-300"
>
<ChevronUp class="h-5 w-5 -rotate-90" />
<span class="sr-only">{{ t("ui.prev-page") }}</span>
</div>
</NuxtLink>
<div v-else class="w-fit cursor-not-allowed rounded border p-2 text-gray-400 transition">
<ChevronUp class="h-5 w-5 -rotate-90" />
<span class="sr-only">{{ t("ui.prev-page") }}</span>
<span class="sr-only">{{ t("ui.first-page") }}</span>
</div>
<div class="flex flex-wrap justify-center gap-1">
<NuxtLink v-if="page > 3" class="underline" :to="{ query: { ...route.query, page: 1 } }"
>1</NuxtLink
>
<span v-if="page > 4">...</span>
<template v-for="n in 5" :key="n - 3 + page">
<NuxtLink
v-if="n - 3 + page >= 1 && n - 3 + page <= Math.ceil(all / limit)"
:class="n === 3 ? 'font-semibold' : 'underline'"
:to="{ query: { ...route.query, page: n - 3 + page } }"
>
{{ n - 3 + page }}
</NuxtLink>
</template>
<span v-if="page + 3 < Math.ceil(all / limit)">...</span>
<NuxtLink
v-if="page + 2 < Math.ceil(all / limit)"
class="underline"
:to="{ query: { ...route.query, page: Math.ceil(all / limit) } }"
>
{{ Math.ceil(all / limit) }}
</NuxtLink>
</div>
</NuxtLink>
<div v-else class="cursor-not-allowed rounded border p-2 text-gray-400 transition">
<ChevronUp class="h-5 w-5 -rotate-90" />
<span class="sr-only">{{ t("ui.first-page") }}</span>
</div>
<div v-if="all != 0">
{{
t("ui.showing-results", {
first: (page - 1) * limit + 1,
last: Math.min(page * limit, all),
all,
})
}}
<div class="mx-auto">
<div v-if="all != 0">
<div>
{{
t("ui.showing-results", {
first: (page - 1) * limit + 1,
last: Math.min(page * limit, all),
all,
})
}}
</div>
</div>
<div v-else class="italic">{{ t("ui.no-results") }}</div>
</div>
<div v-else class="italic">{{ t("ui.no-results") }}</div>
<NuxtLink
v-if="page * limit < Number(all)"
data-testid="nextPage"
:to="{
query: {
...route.query,
page: page + 1,
},
}"
>
<div
class="cursor-pointer rounded border p-2 transition hover:bg-slate-200 active:bg-slate-300"
>
<ChevronUp class="h-5 w-5 rotate-90" />
<span class="sr-only">{{ t("ui.next-page") }}</span>
<div class="flex items-center justify-end gap-2">
<div class="flex items-center gap-1">
<span>{{ t("ui.per-page") }}:</span>
<NuxtLink
v-for="option in [10, 25, 50, 100]"
:key="option"
:class="
(!route.query.limit && option === 25) || option === Number(route.query.limit)
? 'font-semibold'
: 'underline'
"
:to="{
query: {
...route.query,
page: 1,
limit: option,
},
}"
>
{{ option }}
</NuxtLink>
</div>
</NuxtLink>

<div v-else class="cursor-not-allowed rounded border p-2 transition">
<ChevronUp class="h-5 w-5 rotate-90 text-gray-400" />
<span class="sr-only">{{ t("ui.last-page") }}</span>
<NuxtLink
v-if="page * limit < Number(all)"
data-testid="nextPage"
:to="{
query: {
...route.query,
page: page + 1,
},
}"
>
<div
class="cursor-pointer rounded border p-2 transition hover:bg-slate-200 active:bg-slate-300"
>
<ChevronUp class="h-5 w-5 rotate-90" />
<span class="sr-only">{{ t("ui.next-page") }}</span>
</div>
</NuxtLink>

<div v-else class="cursor-not-allowed rounded border p-2 transition">
<ChevronUp class="h-5 w-5 rotate-90 text-gray-400" />
<span class="sr-only">{{ t("ui.last-page") }}</span>
</div>
</div>
</div>
</template>
30 changes: 20 additions & 10 deletions locales/de.json
Original file line number Diff line number Diff line change
Expand Up @@ -24,8 +24,8 @@
},
"searchviews": {
"courts": {
"title": "Hofstaaten",
"label": "Hofstaaten",
"title": "Hofstaate",
"label": "Hofstaate",
"sing": "Hofstaat",
"detail": "{title} - Hofstaat"
},
Expand Down Expand Up @@ -69,13 +69,17 @@
"generic": {
"name": "Name",
"start_date": "Start",
"end_date": "End"
"end_date": "End",
"folio": "Folio",
"kind": "Art",
"shortTitle": "Titel (kurz)",
"title": "Titel"
},
"viecpro_persons": {
"end": "Sterbedatum",
"start": "Geburtsdatum",
"gender": "Geschlecht",
"first_name": "Vorname(n)",
"first_name": "Vorname",
"name": "Nachname",
"fullname": "Name",
"relation_type": "Art des Bezuges",
Expand All @@ -93,11 +97,13 @@
"download": "Download und Zitierweise",
"court_functions": "Funktionen am Hof",
"person_relations_court": "Personenbeziehungen am Hof",
"other_relations_court": "Sonstiger Bezug zum Hof",
"other_relations_court": "Sonstige Bezüge zum Hof",
"marriages_and_family_relations": "Ehe- und Verwandschaftsverhältnisse",
"relations_to_church_and_orders": "Bezug zu Kirche und Orden",
"relations_to_church_and_orders": "Bezüge zu Kirche und Orden",
"non_court_functions": "Sonstige Tätigkeiten",
"model": "Modell"
"model": "Modell",
"functions": "Funktion",
"institutions": "Institution"
},
"viecpro_courts": {
"model": "Modell",
Expand All @@ -111,7 +117,7 @@
"main_owner.start_date": "Geburtsjahr des Hauptinhabers",
"main_owner.model": "Modell des Hauptinhabers",
"main_owner.object_id": "ID des Hauptinhabers",
"main_owner.relation_type": "Bezug zum Hauptinhaber",
"main_owner.relation_type": "Bezüge zum Hauptinhaber",
"owner.start_date": "Geburtsjahr des Besitzers",
"owner.end_date": "Sterbejahr des Besitzers",
"owner.name": "Name des Besitzers",
Expand Down Expand Up @@ -154,7 +160,10 @@
"label:Auflösung": "Auflösung",
"label:Kategorie": "Kategorie",
"start_date": "Beginn",
"end_date": "Ende"
"end_date": "Ende",
"bibtex.title": "Titel",
"bibtex.type": "Art",
"folio": "Folio"
},
"viecpro_events": {
"model": "Modell",
Expand Down Expand Up @@ -193,7 +202,7 @@
"datasheet": "Datenblatt",
"runtime": "Laufzeit",
"additional": "Weitere Informationen",
"courtrelated": "Bezug zum Wiener Hof",
"courtrelated": "Bezüge zum Wiener Hof",
"relations": "Bezüge",
"coords": "Koordinaten"
},
Expand All @@ -204,6 +213,7 @@
"prev-page": "Vorherige Seite",
"first-page": "Es gibt keine Seite vor dieser.",
"last-page": "Das ist bereits die letzte Seite.",
"per-page": "pro Seite",
"show-all": "Zeige alle...",
"show-filters": "Filter ausfahren...",
"hide-filters": "Filter verstecken...",
Expand Down
Loading

0 comments on commit f90f411

Please sign in to comment.