151 lines
4.9 KiB
Vue
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

<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>