mirror of
https://gitlab.com/PronounsPage/PronounsPage.git
synced 2025-08-03 02:56:45 -04:00
151 lines
4.9 KiB
Vue
151 lines
4.9 KiB
Vue
<script setup lang="ts" generic="T extends {id: string}">
|
||
type VPage = {
|
||
page: number;
|
||
text: string | number;
|
||
enabled: boolean;
|
||
} | {
|
||
page?: never;
|
||
text: string;
|
||
enabled: false;
|
||
};
|
||
|
||
const props = withDefaults(defineProps<{
|
||
data: T[];
|
||
perPage?: number;
|
||
marked?: (element: T) => boolean;
|
||
fixed?: boolean;
|
||
count?: boolean;
|
||
}>(), {
|
||
perPage: 30,
|
||
});
|
||
const page = ref(0);
|
||
|
||
const dataPage = computed((): T[] => {
|
||
return props.data.slice(page.value * props.perPage, (page.value + 1) * props.perPage);
|
||
});
|
||
const pages = computed((): number => {
|
||
return Math.ceil(props.data.length / props.perPage);
|
||
});
|
||
const pagesRange = computed((): VPage[] => {
|
||
const vPages = [];
|
||
vPages.push({ page: 0, text: '«', enabled: page.value > 0 });
|
||
vPages.push({ page: page.value - 1, text: '‹', enabled: page.value > 0 });
|
||
for (let i = 0; i < pages.value; i++) {
|
||
if (i <= 4 || (page.value - 3 <= i && i <= page.value + 3) || i >= pages.value - 3) {
|
||
vPages.push({ page: i, text: i + 1, enabled: true });
|
||
} else if (vPages[vPages.length - 1].text !== '…') {
|
||
vPages.push({ text: '…', enabled: false } as const);
|
||
}
|
||
}
|
||
vPages.push({ page: page.value + 1, text: '›', enabled: page.value < pages.value - 1 });
|
||
vPages.push({ page: pages.value - 1, text: '»', enabled: page.value < pages.value - 1 });
|
||
return vPages;
|
||
});
|
||
|
||
const navigateToPage = (to: number) => {
|
||
page.value = to;
|
||
focus();
|
||
};
|
||
|
||
const section = useTemplateRef<HTMLElement>('section');
|
||
const focus = () => {
|
||
section.value?.scrollIntoView();
|
||
};
|
||
|
||
defineExpose({
|
||
reset() {
|
||
navigateToPage(0);
|
||
},
|
||
focus,
|
||
});
|
||
</script>
|
||
|
||
<template>
|
||
<section ref="section" class="table-responsive scroll-mt-7">
|
||
<div class="container" role="table">
|
||
<nav v-if="pages > 1" class="d-flex justify-content-center p-2">
|
||
<ul class="pagination pagination-sm justify-content-center mb-0">
|
||
<li
|
||
v-for="p in pagesRange"
|
||
:key="p.text"
|
||
:class="['page-item', p.page === page ? 'active' : '', p.enabled ? '' : 'disabled']"
|
||
>
|
||
<button v-if="p.enabled" type="button" class="page-link" @click="navigateToPage(p.page)">
|
||
{{ p.text }}
|
||
</button>
|
||
<span v-else class="page-link">
|
||
{{ p.text }}
|
||
</span>
|
||
</li>
|
||
</ul>
|
||
</nav>
|
||
<div v-if="count" class="d-flex">
|
||
<div class="col">
|
||
<T>table.count</T><T>quotation.colon</T>
|
||
<strong>{{ data.length }}</strong>
|
||
</div>
|
||
</div>
|
||
<div class="row-header p-2 d-grid gap-2 border-top" role="row">
|
||
<slot name="header"></slot>
|
||
</div>
|
||
<template v-if="data.length">
|
||
<div
|
||
v-for="el in dataPage"
|
||
:key="el.id"
|
||
:class="['row-content p-2 d-grid gap-2 border-top', marked?.(el) ? 'marked' : '']"
|
||
role="row"
|
||
>
|
||
<slot name="row" :el="el"></slot>
|
||
</div>
|
||
</template>
|
||
<template v-else>
|
||
<div class="d-flex">
|
||
<div class="text-center col">
|
||
<slot name="empty">
|
||
<Icon v="search" />
|
||
<T>table.empty</T>
|
||
</slot>
|
||
</div>
|
||
</div>
|
||
</template>
|
||
<nav v-if="pages > 1" class="d-flex justify-content-center p-2 border-top">
|
||
<ul class="pagination pagination-sm justify-content-center">
|
||
<li
|
||
v-for="p in pagesRange"
|
||
:key="p.text"
|
||
:class="['page-item', p.page === page ? 'active' : '', p.enabled ? '' : 'disabled']"
|
||
>
|
||
<button v-if="p.enabled" type="button" class="page-link" @click="navigateToPage(p.page)">
|
||
{{ p.text }}
|
||
</button>
|
||
<span v-else class="page-link">
|
||
{{ p.text }}
|
||
</span>
|
||
</li>
|
||
</ul>
|
||
</nav>
|
||
</div>
|
||
</section>
|
||
</template>
|
||
|
||
<style lang="scss">
|
||
@import "assets/variables";
|
||
|
||
.marked {
|
||
border-inline-start: 3px solid $primary;
|
||
}
|
||
|
||
.row-content {
|
||
background-color: var(--bs-table-bg);
|
||
border-bottom-width: var(--bs-border-width);
|
||
|
||
&:nth-child(odd) {
|
||
background: rgba(var(--bs-emphasis-color-rgb), 0.05);
|
||
}
|
||
|
||
&:hover {
|
||
background: rgba(var(--bs-emphasis-color-rgb), 0.075);
|
||
}
|
||
}
|
||
</style>
|