feat(Components): Add pagination component
This component accept a Page<T> type from [astro](https://docs.astro.build/en/guides/routing/#complete-api-reference)
This commit is contained in:
parent
642e15656e
commit
944b553e73
9 changed files with 258 additions and 83 deletions
|
|
@ -1,24 +1,36 @@
|
|||
---
|
||||
interface Props {
|
||||
className?: string;
|
||||
href?: string;
|
||||
}
|
||||
|
||||
const { className = '' } = Astro.props;
|
||||
const { className = '', href } = Astro.props;
|
||||
---
|
||||
|
||||
<button class:list={className}>
|
||||
<slot />
|
||||
</button>
|
||||
{
|
||||
href !== undefined ? (
|
||||
<a href={href} class:list={['clean', 'btn', className]}>
|
||||
<slot />
|
||||
</a>
|
||||
) : (
|
||||
<button class:list={className}>
|
||||
<slot />
|
||||
</button>
|
||||
)
|
||||
}
|
||||
|
||||
<style lang="scss">
|
||||
button {
|
||||
button,
|
||||
.btn {
|
||||
display: inline-block;
|
||||
text-decoration: none;
|
||||
font-size: 1rem;
|
||||
padding: var(--prj-spacing-1) var(--prj-spacing-3);
|
||||
background-color: var(--prj-accent-bg);
|
||||
color: var(--prj-accent-text);
|
||||
|
||||
border-radius: 6px;
|
||||
border-color: var(--prj-accent-bg);
|
||||
border: 1px solid var(--prj-accent-bg);
|
||||
|
||||
cursor: pointer;
|
||||
|
||||
|
|
|
|||
|
|
@ -52,25 +52,25 @@ const links = [
|
|||
</div>
|
||||
|
||||
<script>
|
||||
const normilizeUrl = (url: string): string => {
|
||||
let newUrl = `${url}`;
|
||||
|
||||
if (newUrl.endsWith('/')) {
|
||||
newUrl = newUrl.slice(0, -1);
|
||||
}
|
||||
|
||||
return newUrl;
|
||||
};
|
||||
|
||||
const setActiveLink = () => {
|
||||
const links =
|
||||
document.querySelectorAll<HTMLAnchorElement>(`#main-navbar a`);
|
||||
|
||||
links.forEach((link) =>
|
||||
normilizeUrl(link.pathname) === normilizeUrl(location.pathname)
|
||||
links.forEach((link) => {
|
||||
if (link.pathname === '/' && location.pathname === '/') {
|
||||
link.classList.add('active');
|
||||
return;
|
||||
}
|
||||
|
||||
if (link.pathname === '/' && location.pathname !== '/') {
|
||||
link.classList.remove('active');
|
||||
return;
|
||||
}
|
||||
|
||||
location.pathname.startsWith(link.pathname)
|
||||
? link.classList.add('active')
|
||||
: link.classList.remove('active'),
|
||||
);
|
||||
: link.classList.remove('active');
|
||||
});
|
||||
};
|
||||
// Add active class to the current link
|
||||
document.addEventListener('astro:page-load', setActiveLink, { once: true });
|
||||
|
|
|
|||
133
src/components/Pagination.astro
Normal file
133
src/components/Pagination.astro
Normal file
|
|
@ -0,0 +1,133 @@
|
|||
---
|
||||
import { type Page } from 'astro';
|
||||
|
||||
interface Props {
|
||||
page: Page;
|
||||
paginationOffset?: number;
|
||||
urlPattern: string;
|
||||
}
|
||||
|
||||
const { page, urlPattern, paginationOffset = 3 } = Astro.props;
|
||||
|
||||
const pages = [];
|
||||
|
||||
const lowerEnd = Math.max(page.currentPage - paginationOffset, 1);
|
||||
const highEnd = Math.min(page.currentPage + paginationOffset, page.lastPage);
|
||||
|
||||
const generateUrl = (index: number) => {
|
||||
return urlPattern.replace('{}', index.toString());
|
||||
};
|
||||
|
||||
for (let index = lowerEnd; index <= highEnd; index++) {
|
||||
pages.push({
|
||||
index,
|
||||
url: generateUrl(index),
|
||||
});
|
||||
}
|
||||
---
|
||||
|
||||
<nav role="navigation" aria-label="Pagination" class="w-100 my-4">
|
||||
<ul class="list-unstyle hstack justify-content-center">
|
||||
{
|
||||
page.url.prev !== undefined && (
|
||||
<li>
|
||||
<a
|
||||
href={page.url.prev}
|
||||
class="prev-page"
|
||||
aria-label="Go to previous page"
|
||||
>
|
||||
« Prev
|
||||
</a>
|
||||
</li>
|
||||
)
|
||||
}{
|
||||
lowerEnd !== 1 && (
|
||||
<>
|
||||
<li>
|
||||
<a
|
||||
href={generateUrl(1)}
|
||||
class="prev-page"
|
||||
aria-label={`Go to page 1`}
|
||||
>
|
||||
1
|
||||
</a>
|
||||
</li>
|
||||
<li>
|
||||
<span class="start-ellipsis">…</span>
|
||||
</li>
|
||||
</>
|
||||
)
|
||||
}
|
||||
{
|
||||
pages.map((item) => (
|
||||
<li>
|
||||
<a
|
||||
class:list={[{ current: item.index === page.currentPage }]}
|
||||
href={item.url}
|
||||
aria-label={`Go to page ${item.index}`}
|
||||
>
|
||||
{item.index}
|
||||
</a>
|
||||
</li>
|
||||
))
|
||||
}
|
||||
{
|
||||
highEnd !== page.lastPage && (
|
||||
<>
|
||||
<li>
|
||||
<span class="start-ellipsis">…</span>
|
||||
</li>
|
||||
<li>
|
||||
<a
|
||||
href={generateUrl(page.lastPage)}
|
||||
class="next-page"
|
||||
aria-label={`Go to page ${page.lastPage}`}
|
||||
>
|
||||
{page.lastPage}
|
||||
</a>
|
||||
</li>
|
||||
</>
|
||||
)
|
||||
}
|
||||
{
|
||||
page.url.next !== undefined && (
|
||||
<li>
|
||||
<a
|
||||
href={page.url.next}
|
||||
class="next-page"
|
||||
aria-label="Go to next page"
|
||||
>
|
||||
Next »
|
||||
</a>
|
||||
</li>
|
||||
)
|
||||
}
|
||||
</ul>
|
||||
</nav>
|
||||
|
||||
<style lang="scss">
|
||||
li {
|
||||
margin-bottom: 0;
|
||||
}
|
||||
|
||||
a {
|
||||
border: 1px solid var(--prj-link-text);
|
||||
padding: var(--prj-spacing-1) var(--prj-spacing-2);
|
||||
border-radius: var(--prj-border-radius);
|
||||
text-decoration: none;
|
||||
transition: background-color 400ms, color 400ms;
|
||||
|
||||
&.current {
|
||||
background-color: var(--prj-secondary);
|
||||
border: 1px solid var(--prj-secondary);
|
||||
color: var(--prj-secondary-text);
|
||||
}
|
||||
|
||||
&:hover {
|
||||
background-color: var(--prj-link-text);
|
||||
border: 1px solid var(--prj-link-text);
|
||||
color: var(--prj-accent-text);
|
||||
text-shadow: none;
|
||||
}
|
||||
}
|
||||
</style>
|
||||
Loading…
Add table
Add a link
Reference in a new issue