Skip to content

Commit

Permalink
Merge pull request #279 from thekhegay/feat-pagination
Browse files Browse the repository at this point in the history
feat: wr-pagination component
  • Loading branch information
thekhegay authored Feb 3, 2025
2 parents f59a4ca + 949853b commit 9f2e081
Show file tree
Hide file tree
Showing 16 changed files with 649 additions and 0 deletions.
3 changes: 3 additions & 0 deletions projects/lib/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -84,6 +84,9 @@
},
"./select/styles": {
"sass": "./select/styles/_index.scss"
},
"./pagination/styles": {
"sass": "./pagination/styles/_index.scss"
}
}
}
8 changes: 8 additions & 0 deletions projects/lib/pagination/index.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
/**
* @license
*
* Use of this source code is governed by an MIT-style license that can be
* found in the LICENSE file at https://github.com/thekhegay/ngwr/blob/main/LICENSE
*/

export * from './public-api';
5 changes: 5 additions & 0 deletions projects/lib/pagination/ng-package.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
{
"lib": {
"entryFile": "public-api.ts"
}
}
46 changes: 46 additions & 0 deletions projects/lib/pagination/pagination.component.html
Original file line number Diff line number Diff line change
@@ -0,0 +1,46 @@
<div class="wr-pagination-container" [class]="'wr-pagination--' + position()">
@if (showTotal()) {
<span class="wr-pagination-total">
{{ currentRange() }}
</span>
}

<div class="wr-pagination-items">
<wr-btn
outlined
[disabled]="disabled() || currentPage() === 1"
(click)="onPageChange(currentPage() - 1)"
icon="arrow-back"
></wr-btn>

@for (page of pages(); track page) {
@if (page === '...') {
<span class="wr-pagination-ellipsis">...</span>
} @else {
<wr-btn
[outlined]="!isCurrentPage(page)"
[color]="isCurrentPage(page) ? 'primary' : 'medium'"
[disabled]="disabled()"
(click)="onPageChange(page)"
>
{{ page }}
</wr-btn>
}
}

<wr-btn
outlined
[disabled]="disabled() || currentPage() === totalPages()"
(click)="onPageChange(currentPage() + 1)"
icon="arrow-forward"
></wr-btn>
</div>

@if (showSizeChanger()) {
<wr-select [disabled]="disabled()" [ngModel]="pageSize()" (ngModelChange)="onPageSizeChange($event)">
@for (option of pageSizeOptions(); track option) {
<wr-option [value]="option" [label]="String(option)"></wr-option>
}
</wr-select>
}
</div>
135 changes: 135 additions & 0 deletions projects/lib/pagination/pagination.component.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,135 @@
/**
* @license
*
* Use of this source code is governed by an MIT-style license that can be
* found in the LICENSE file at https://github.com/thekhegay/ngwr/blob/main/LICENSE
*/

import {
ChangeDetectionStrategy,
Component,
ViewEncapsulation,
booleanAttribute,
computed,
input,
model,
numberAttribute,
HostBinding,
} from '@angular/core';
import { FormsModule } from '@angular/forms';

import { WrButtonComponent } from 'ngwr/button';
import { provideWrIcons, arrowBack, arrowForward } from 'ngwr/icon';
import { WrSelectComponent } from 'ngwr/select';
import { WrOptionComponent } from 'ngwr/select/select-option.component';

import { WrPaginationPosition } from './pagination.types';

@Component({
selector: 'wr-pagination',
standalone: true,
imports: [WrButtonComponent, WrSelectComponent, WrOptionComponent, FormsModule],
templateUrl: './pagination.component.html',
styleUrl: './pagination.component.scss',
changeDetection: ChangeDetectionStrategy.OnPush,
encapsulation: ViewEncapsulation.None,
providers: [provideWrIcons([arrowBack, arrowForward])],
})
export class WrPaginationComponent {
currentPage = model<number>(1);
total = input<number, number>(0, { transform: numberAttribute });
pageSize = model<number>(10);
pageSizeOptions = input<number[]>([10, 20, 50, 100]);
showSizeChanger = input<boolean, boolean>(false, { transform: booleanAttribute });
showTotal = input<boolean, boolean>(false, { transform: booleanAttribute });
position = input<WrPaginationPosition>('start');
disabled = input<boolean, boolean>(false, { transform: booleanAttribute });
ofLabel = input<string>('of');

@HostBinding('class')
get hostClasses(): Record<string, boolean> {
return {
'wr-pagination': true,
'wr-pagination--disabled': this.disabled(),
};
}

protected readonly String = String;

protected readonly totalPages = computed(() => Math.ceil(this.total() / this.pageSize()));

protected readonly currentRange = computed(() => {
const start = (this.currentPage() - 1) * this.pageSize() + 1;
const end = Math.min(this.currentPage() * this.pageSize(), this.total());
return `${start}-${end} ${this.ofLabel()} ${this.total()}`;
});

protected readonly pages = computed(() => {
const total = this.totalPages();
const current = this.currentPage();
const items: (number | '...')[] = [];

if (total <= 7) {
return Array.from({ length: total }, (_, i) => i + 1);
}

items.push(1);

if (current > 4) {
items.push('...');
}

let start: number;
let end: number;

if (current <= 4) {
start = 2;
end = 5;
} else if (current >= total - 3) {
start = total - 4;
end = total - 1;
} else {
start = current - 2;
end = current + 2;
}

for (let i = start; i <= end; i++) {
items.push(i);
}

if (current < total - 3) {
items.push('...');
}

if (end !== total) {
items.push(total);
}

return items;
});

protected isCurrentPage(page: number): boolean {
return this.currentPage() === page;
}

protected onPageChange(page: number): void {
if (this.disabled() || page === this.currentPage() || page < 1 || page > this.totalPages()) {
return;
}

this.currentPage.set(page);
}

protected onPageSizeChange(size: number): void {
if (this.disabled() || size === this.pageSize()) {
return;
}

this.pageSize.set(size);

const newTotalPages = Math.ceil(this.total() / size);
if (this.currentPage() > newTotalPages) {
this.onPageChange(newTotalPages);
}
}
}
8 changes: 8 additions & 0 deletions projects/lib/pagination/pagination.types.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
/**
* @license
*
* Use of this source code is governed by an MIT-style license that can be
* found in the LICENSE file at https://github.com/thekhegay/ngwr/blob/main/LICENSE
*/

export type WrPaginationPosition = 'start' | 'center' | 'end';
9 changes: 9 additions & 0 deletions projects/lib/pagination/public-api.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
/**
* @license
*
* Use of this source code is governed by an MIT-style license that can be
* found in the LICENSE file at https://github.com/thekhegay/ngwr/blob/main/LICENSE
*/

export * from './pagination.types';
export * from './pagination.component';
60 changes: 60 additions & 0 deletions projects/lib/pagination/styles/_index.scss
Original file line number Diff line number Diff line change
@@ -0,0 +1,60 @@
@forward "./vars";

.wr-pagination {
--wr-select-option-height: var(--wr-pagination-select-option-height);
--wr-select-padding-y: var(--wr-pagination-select-padding-y);
--wr-select-padding-x: var(--wr-pagination-select-padding-x);

display: block;

&-container {
display: flex;
align-items: center;
gap: var(--wr-pagination-gap);
}

&--start {
justify-content: flex-start;
}

&--center {
justify-content: center;
}

&--end {
justify-content: flex-end;
}

&--disabled {
opacity: 0.6;
pointer-events: none;
}

&-total {
color: var(--wr-pagination-total-color);
font-size: var(--wr-pagination-font-size);
}

&-items {
display: flex;
align-items: center;
gap: var(--wr-pagination-items-gap);
}

&-ellipsis {
color: var(--wr-pagination-ellipsis-color);
padding: 0 var(--wr-pagination-item-padding);
user-select: none;
}

wr-btn {
display: flex;
align-items: center;
justify-content: center;
min-width: var(--wr-pagination-item-size);
height: var(--wr-pagination-item-size);
padding: 0;
gap: 0;
text-align: center;
}
}
14 changes: 14 additions & 0 deletions projects/lib/pagination/styles/vars.scss
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
:root {
--wr-pagination-gap: 1rem;
--wr-pagination-items-gap: 0.5rem;
--wr-pagination-font-size: 0.875rem;
--wr-pagination-total-color: var(--wr-color-medium);
--wr-pagination-ellipsis-color: var(--wr-color-medium);

--wr-pagination-item-size: 1.625rem;
--wr-pagination-item-padding: 0.5rem;

--wr-pagination-select-option-height: var(--wr-pagination-item-size);
--wr-pagination-select-padding-y: 0;
--wr-pagination-select-padding-x: 0.825rem;
}
1 change: 1 addition & 0 deletions projects/lib/styles.scss
Original file line number Diff line number Diff line change
Expand Up @@ -22,4 +22,5 @@
@forward './tag/styles';
@forward './textarea/styles';
@forward './select/styles';
@forward './pagination/styles';
//@forward './tooltip';
Original file line number Diff line number Diff line change
Expand Up @@ -74,6 +74,7 @@ export class SidebarComponent extends WrAbstractBase implements OnInit {
{ title: 'Tag', url: [routes.docs.components.index, routes.docs.components.tag] },
{ title: 'Textarea', url: [routes.docs.components.index, routes.docs.components.textarea] },
{ title: 'Select', url: [routes.docs.components.index, routes.docs.components.select] },
{ title: 'Pagination', url: [routes.docs.components.index, routes.docs.components.pagination] },
],
},
];
Expand Down
4 changes: 4 additions & 0 deletions projects/showcase/app/docs/components/components.routing.ts
Original file line number Diff line number Diff line change
Expand Up @@ -78,4 +78,8 @@ export default [
path: components.select,
loadComponent: () => import('./select/select.component').then(c => c.SelectComponent),
},
{
path: components.pagination,
loadComponent: () => import('./pagination/pagination.component').then(c => c.PaginationComponent),
},
] satisfies Routes;
Loading

0 comments on commit 9f2e081

Please sign in to comment.