changes to UI mostly
This commit is contained in:
@@ -1,76 +1,200 @@
|
||||
<script setup>
|
||||
import { Link } from '@inertiajs/vue3';
|
||||
import { Link } from "@inertiajs/vue3";
|
||||
import { computed } from "vue";
|
||||
|
||||
const props = defineProps({
|
||||
links: Array,
|
||||
from: Number,
|
||||
to: Number,
|
||||
total: Number
|
||||
links: { type: Array, default: () => [] },
|
||||
from: { type: Number, default: 0 },
|
||||
to: { type: Number, default: 0 },
|
||||
total: { type: Number, default: 0 },
|
||||
});
|
||||
|
||||
const num = props.links.length;
|
||||
const num = props.links?.length || 0;
|
||||
|
||||
const prevLink = computed(() => (num > 0 ? props.links[0] : null));
|
||||
const nextLink = computed(() => (num > 1 ? props.links[num - 1] : null));
|
||||
const numericLinks = computed(() => {
|
||||
if (num < 3) return [];
|
||||
return props.links
|
||||
.slice(1, num - 1)
|
||||
.map((l) => ({
|
||||
...l,
|
||||
page: Number.parseInt(String(l.label).replace(/[^0-9]/g, ""), 10),
|
||||
}))
|
||||
.filter((l) => !Number.isNaN(l.page));
|
||||
});
|
||||
const currentPage = computed(() => numericLinks.value.find((l) => l.active)?.page || 1);
|
||||
const lastPage = computed(() =>
|
||||
numericLinks.value.length ? Math.max(...numericLinks.value.map((l) => l.page)) : 1
|
||||
);
|
||||
const linkByPage = computed(() => {
|
||||
const m = new Map();
|
||||
for (const l of numericLinks.value) m.set(l.page, l);
|
||||
return m;
|
||||
});
|
||||
const windowItems = computed(() => {
|
||||
const items = [];
|
||||
const cur = currentPage.value;
|
||||
const last = lastPage.value;
|
||||
const show = new Set([1, last, cur - 1, cur, cur + 1]);
|
||||
if (cur <= 3) {
|
||||
show.add(2);
|
||||
show.add(3);
|
||||
}
|
||||
if (cur >= last - 2) {
|
||||
show.add(last - 1);
|
||||
show.add(last - 2);
|
||||
}
|
||||
|
||||
// Prev
|
||||
items.push({ kind: "prev", link: prevLink.value });
|
||||
|
||||
// Pages with ellipses
|
||||
let inGap = false;
|
||||
for (let p = 1; p <= last; p++) {
|
||||
if (show.has(p)) {
|
||||
items.push({
|
||||
kind: "page",
|
||||
link: linkByPage.value.get(p) || {
|
||||
url: null,
|
||||
label: String(p),
|
||||
active: p === cur,
|
||||
},
|
||||
});
|
||||
inGap = false;
|
||||
} else if (!inGap) {
|
||||
items.push({ kind: "ellipsis" });
|
||||
inGap = true;
|
||||
}
|
||||
}
|
||||
|
||||
// Next
|
||||
items.push({ kind: "next", link: nextLink.value });
|
||||
|
||||
return items;
|
||||
});
|
||||
</script>
|
||||
<template>
|
||||
<div class="flex items-center justify-between bg-white px-4 py-3 sm:px-6">
|
||||
<div class="flex flex-1 justify-between sm:hidden">
|
||||
<a href="#" class="relative inline-flex items-center rounded-md border border-gray-300 bg-white px-4 py-2 text-sm font-medium text-gray-700 hover:bg-gray-50">Previous</a>
|
||||
<a href="#" class="relative ml-3 inline-flex items-center rounded-md border border-gray-300 bg-white px-4 py-2 text-sm font-medium text-gray-700 hover:bg-gray-50">Next</a>
|
||||
</div>
|
||||
<div class="hidden sm:flex sm:flex-1 sm:items-center sm:justify-between">
|
||||
<div>
|
||||
<p class="text-sm text-gray-700">
|
||||
Showing
|
||||
<span class="font-medium">{{ from }}</span>
|
||||
to
|
||||
<span class="font-medium">{{ to }}</span>
|
||||
of
|
||||
<span class="font-medium">{{ total }}</span>
|
||||
results
|
||||
</p>
|
||||
</div>
|
||||
<div>
|
||||
<nav class="isolate inline-flex -space-x-px rounded-md shadow-sm" aria-label="Pagination">
|
||||
<template v-for="(link, index) in links">
|
||||
<component
|
||||
:is="link.url ? Link : 'span'"
|
||||
v-if="index === 0 || index === (num - 1)"
|
||||
:href="link.url"
|
||||
class="relative inline-flex items-center px-2 py-2 ring-1 ring-inset ring-gray-300 focus:z-20 focus:outline-offset-0"
|
||||
:class="{
|
||||
'rounded-l-md': index === 0,
|
||||
'rounded-r-md': index === (num - 1),
|
||||
'text-gray-900 hover:bg-gray-50': link.url,
|
||||
'text-gray-400 bg-gray-100': ! link.url}"
|
||||
>
|
||||
|
||||
<span class="sr-only">
|
||||
{{ index === 0 ? 'Previous' : 'Next' }}
|
||||
</span>
|
||||
<svg v-if="index === 0" class="h-5 w-5" viewBox="0 0 20 20" fill="currentColor" aria-hidden="true" data-slot="icon">
|
||||
<path fill-rule="evenodd" d="M11.78 5.22a.75.75 0 0 1 0 1.06L8.06 10l3.72 3.72a.75.75 0 1 1-1.06 1.06l-4.25-4.25a.75.75 0 0 1 0-1.06l4.25-4.25a.75.75 0 0 1 1.06 0Z" clip-rule="evenodd" />
|
||||
</svg>
|
||||
<svg v-else class="h-5 w-5" viewBox="0 0 20 20" fill="currentColor" aria-hidden="true" data-slot="icon">
|
||||
<path fill-rule="evenodd" d="M8.22 5.22a.75.75 0 0 1 1.06 0l4.25 4.25a.75.75 0 0 1 0 1.06l-4.25 4.25a.75.75 0 0 1-1.06-1.06L11.94 10 8.22 6.28a.75.75 0 0 1 0-1.06Z" clip-rule="evenodd" />
|
||||
</svg>
|
||||
</component>
|
||||
<component
|
||||
v-else
|
||||
:is="link.url ? Link : 'span'"
|
||||
:href="link.url"
|
||||
v-html="link.label"
|
||||
class="relative inline-flex items-center px-4 py-2 text-sm font-semibold focus:outline-offset-0"
|
||||
:class="{
|
||||
'text-gray-700 ring-1 ring-inset ring-gray-300': ! link.url,
|
||||
'text-gray-900 ring-1 ring-inset ring-gray-300 hover:bg-gray-50 focus:z-20': link.url && ! link.active,
|
||||
'z-10 bg-blue-600 text-white focus:z-20 focus-visible:outline focus-visible:outline-2 focus-visible:outline-offset-2 focus-visible:outline-blue-600': link.url && link.active
|
||||
}"
|
||||
>
|
||||
|
||||
</component>
|
||||
</template>
|
||||
</nav>
|
||||
</div>
|
||||
</div>
|
||||
<div class="flex items-center justify-between bg-white px-4 py-3 sm:px-6">
|
||||
<!-- Mobile: Prev / Next -->
|
||||
<div class="flex flex-1 justify-between sm:hidden">
|
||||
<component
|
||||
:is="links?.[0]?.url ? Link : 'span'"
|
||||
:href="links?.[0]?.url"
|
||||
:aria-disabled="!links?.[0]?.url"
|
||||
:tabindex="!links?.[0]?.url ? -1 : 0"
|
||||
class="relative inline-flex items-center rounded-md border px-4 py-2 text-sm font-medium"
|
||||
:class="
|
||||
links?.[0]?.url
|
||||
? 'border-gray-300 bg-white text-gray-700 hover:bg-gray-50'
|
||||
: 'border-gray-200 bg-gray-100 text-gray-400'
|
||||
"
|
||||
>
|
||||
Prejšnja
|
||||
</component>
|
||||
<component
|
||||
:is="links?.[num - 1]?.url ? Link : 'span'"
|
||||
:href="links?.[num - 1]?.url"
|
||||
:aria-disabled="!links?.[num - 1]?.url"
|
||||
:tabindex="!links?.[num - 1]?.url ? -1 : 0"
|
||||
class="relative ml-3 inline-flex items-center rounded-md border px-4 py-2 text-sm font-medium"
|
||||
:class="
|
||||
links?.[num - 1]?.url
|
||||
? 'border-gray-300 bg-white text-gray-700 hover:bg-gray-50'
|
||||
: 'border-gray-200 bg-gray-100 text-gray-400'
|
||||
"
|
||||
>
|
||||
Naslednja
|
||||
</component>
|
||||
</div>
|
||||
</template>
|
||||
<div class="hidden sm:flex sm:flex-1 sm:items-center sm:justify-between">
|
||||
<div>
|
||||
<p class="text-sm text-gray-700">
|
||||
Showing
|
||||
<span class="font-medium">{{ from }}</span>
|
||||
to
|
||||
<span class="font-medium">{{ to }}</span>
|
||||
of
|
||||
<span class="font-medium">{{ total }}</span>
|
||||
results
|
||||
</p>
|
||||
</div>
|
||||
<div>
|
||||
<nav
|
||||
class="isolate inline-flex -space-x-px rounded-md shadow-sm"
|
||||
aria-label="Pagination"
|
||||
>
|
||||
<template v-for="(item, idx) in windowItems" :key="idx">
|
||||
<!-- Prev / Next -->
|
||||
<component
|
||||
v-if="item.kind === 'prev' || item.kind === 'next'"
|
||||
:is="item.link?.url ? Link : 'span'"
|
||||
:href="item.link?.url"
|
||||
class="relative inline-flex items-center px-2 py-2 ring-1 ring-inset ring-gray-300 focus:z-20 focus:outline-offset-0"
|
||||
:class="{
|
||||
'rounded-l-md': item.kind === 'prev',
|
||||
'rounded-r-md': item.kind === 'next',
|
||||
'text-gray-900 hover:bg-gray-50': item.link?.url,
|
||||
'text-gray-400 bg-gray-100': !item.link?.url,
|
||||
}"
|
||||
>
|
||||
<span class="sr-only">{{
|
||||
item.kind === "prev" ? "Prejšnja" : "Naslednja"
|
||||
}}</span>
|
||||
<svg
|
||||
v-if="item.kind === 'prev'"
|
||||
class="h-5 w-5"
|
||||
viewBox="0 0 20 20"
|
||||
fill="currentColor"
|
||||
aria-hidden="true"
|
||||
>
|
||||
<path
|
||||
fill-rule="evenodd"
|
||||
d="M11.78 5.22a.75.75 0 0 1 0 1.06L8.06 10l3.72 3.72a.75.75 0 1 1-1.06 1.06l-4.25-4.25a.75.75 0 0 1 0-1.06l4.25-4.25a.75.75 0 0 1 1.06 0Z"
|
||||
clip-rule="evenodd"
|
||||
/>
|
||||
</svg>
|
||||
<svg
|
||||
v-else
|
||||
class="h-5 w-5"
|
||||
viewBox="0 0 20 20"
|
||||
fill="currentColor"
|
||||
aria-hidden="true"
|
||||
>
|
||||
<path
|
||||
fill-rule="evenodd"
|
||||
d="M8.22 5.22a.75.75 0 0 1 1.06 0l4.25 4.25a.75.75 0 0 1 0 1.06l-4.25 4.25a.75.75 0 0 1-1.06-1.06L11.94 10 8.22 6.28a.75.75 0 0 1 0-1.06Z"
|
||||
clip-rule="evenodd"
|
||||
/>
|
||||
</svg>
|
||||
</component>
|
||||
|
||||
<!-- Ellipsis -->
|
||||
<span
|
||||
v-else-if="item.kind === 'ellipsis'"
|
||||
class="relative inline-flex items-center px-4 py-2 text-sm font-semibold text-gray-500 ring-1 ring-inset ring-gray-200 select-none"
|
||||
>…</span
|
||||
>
|
||||
|
||||
<!-- Page number -->
|
||||
<component
|
||||
v-else-if="item.kind === 'page'"
|
||||
:is="item.link?.url ? Link : 'span'"
|
||||
:href="item.link?.url"
|
||||
class="relative inline-flex items-center px-4 py-2 text-sm font-semibold focus:outline-offset-0"
|
||||
:class="{
|
||||
'text-gray-700 ring-1 ring-inset ring-gray-300': !item.link?.url,
|
||||
'text-gray-900 ring-1 ring-inset ring-gray-300 hover:bg-gray-50 focus:z-20':
|
||||
item.link?.url && !item.link?.active,
|
||||
'z-10 bg-blue-600 text-white focus:z-20 focus-visible:outline focus-visible:outline-2 focus-visible:outline-offset-2 focus-visible:outline-blue-600':
|
||||
item.link?.active,
|
||||
}"
|
||||
>
|
||||
{{ item.link?.label || "" }}
|
||||
</component>
|
||||
</template>
|
||||
</nav>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
Reference in New Issue
Block a user