/**
* @copyright 2026 NHCarrigan
* @license Naomi's Public License
* @author Naomi Carrigan
*/
import { Component, Input, Output, EventEmitter, computed } from '@angular/core';
import { CommonModule } from '@angular/common';
@Component({
selector: 'app-pagination',
standalone: true,
imports: [CommonModule],
template: `
`,
styles: [`
.pagination-container {
display: flex;
justify-content: space-between;
align-items: center;
padding: 1rem 0;
flex-wrap: wrap;
gap: 1rem;
}
.pagination-info {
color: var(--witch-plum);
font-size: 0.9rem;
}
.pagination-controls {
display: flex;
align-items: center;
gap: 0.5rem;
}
.pagination-btn {
padding: 0.25rem 0.75rem;
border: 1px solid var(--witch-lavender);
background: var(--witch-moon);
color: var(--witch-purple);
border-radius: 4px;
cursor: pointer;
transition: all 0.2s;
min-width: 2.5rem;
}
.pagination-btn:hover:not(:disabled) {
background: var(--witch-lavender);
border-color: var(--witch-mauve);
transform: translateY(-1px);
}
.pagination-btn:disabled {
opacity: 0.5;
cursor: not-allowed;
}
.pagination-btn.active {
background: #8b6f47;
color: white;
border-color: #8b6f47;
}
.page-numbers {
display: flex;
align-items: center;
gap: 0.25rem;
}
.page-number {
min-width: 2.5rem;
}
.page-ellipsis {
padding: 0 0.5rem;
color: var(--witch-mauve);
}
.page-size-selector {
display: flex;
align-items: center;
gap: 0.5rem;
}
.page-size-selector label {
font-size: 0.9rem;
color: var(--witch-plum);
}
.page-size-select {
padding: 0.25rem 0.5rem;
border: 2px solid var(--witch-lavender);
border-radius: 4px;
background: var(--witch-moon);
color: var(--witch-purple);
cursor: pointer;
transition: border-color 0.2s;
}
.page-size-select:hover {
border-color: var(--witch-mauve);
}
.page-size-select:focus {
outline: none;
border-color: var(--witch-rose);
box-shadow: 0 0 0 3px rgba(168, 87, 126, 0.2);
}
@media (max-width: 768px) {
.pagination-container {
justify-content: center;
}
.pagination-controls {
order: 2;
width: 100%;
justify-content: center;
}
.pagination-info {
order: 1;
width: 100%;
text-align: center;
}
.page-size-selector {
order: 3;
width: 100%;
justify-content: center;
}
}
`]
})
export class PaginationComponent {
@Input() currentPage = 1;
@Input() pageSize = 25;
@Input() totalItems = 0;
@Output() pageChange = new EventEmitter();
@Output() pageSizeChange = new EventEmitter();
totalPages = computed(() => Math.ceil(this.totalItems / this.pageSize));
startItem = computed(() => {
if (this.totalItems === 0) return 0;
return (this.currentPage - 1) * this.pageSize + 1;
});
endItem = computed(() => {
return Math.min(this.currentPage * this.pageSize, this.totalItems);
});
pageNumbers = computed(() => {
const total = this.totalPages();
const current = this.currentPage;
const pages: (number | string)[] = [];
if (total <= 7) {
// Show all pages if 7 or fewer
for (let i = 1; i <= total; i++) {
pages.push(i);
}
} else {
// Always show first page
pages.push(1);
if (current <= 3) {
// Near the beginning
for (let i = 2; i <= 5; i++) {
pages.push(i);
}
pages.push('...');
pages.push(total);
} else if (current >= total - 2) {
// Near the end
pages.push('...');
for (let i = total - 4; i <= total; i++) {
pages.push(i);
}
} else {
// In the middle
pages.push('...');
pages.push(current - 1);
pages.push(current);
pages.push(current + 1);
pages.push('...');
pages.push(total);
}
}
return pages;
});
onPageChange(page: number) {
if (page >= 1 && page <= this.totalPages() && page !== this.currentPage) {
this.pageChange.emit(page);
}
}
onPageSizeChange(event: Event) {
const target = event.target as HTMLSelectElement;
const newSize = parseInt(target.value, 10);
this.pageSizeChange.emit(newSize);
}
}