Skip to content

Commit

Permalink
feat: wr-pagination component
Browse files Browse the repository at this point in the history
  • Loading branch information
yumagulov-azat committed Feb 2, 2025
1 parent f59a4ca commit 629a667
Show file tree
Hide file tree
Showing 15 changed files with 607 additions and 0 deletions.
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() || current() === 1"
(click)="onPageChange(current() - 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() || current() === totalPages()"
(click)="onPageChange(current() + 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>
130 changes: 130 additions & 0 deletions projects/lib/pagination/pagination.component.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,130 @@
/**
* @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,
} 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])],
host: {
class: 'wr-pagination',
'[class.wr-pagination--disabled]': 'disabled()',
},
})
export class WrPaginationComponent {
total = input<number, number>(0, { transform: numberAttribute });
pageSize = model<number>(10);
current = model<number>(1);
position = input<WrPaginationPosition>('start');
disabled = input<boolean, boolean>(false, { transform: booleanAttribute });
showSizeChanger = input<boolean, boolean>(false, { transform: booleanAttribute });
showTotal = input<boolean, boolean>(false, { transform: booleanAttribute });
pageSizeOptions = input<number[]>([10, 20, 50, 100]);
ofLabel = input<string>('of');

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

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

protected readonly pages = computed(() => {
const total = this.totalPages();
const current = this.current();
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.current() === page;
}

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

this.current.set(page);
}

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

this.pageSize.set(size);

const newTotalPages = Math.ceil(this.total() / size);
if (this.current() > newTotalPages) {
this.onPageChange(newTotalPages);
}
}

protected readonly String = String;
}
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';
56 changes: 56 additions & 0 deletions projects/lib/pagination/styles/_index.scss
Original file line number Diff line number Diff line change
@@ -0,0 +1,56 @@
@forward "./vars";

.wr-pagination {
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;
width: var(--wr-pagination-item-size);
height: var(--wr-pagination-item-size);
padding: 0;
gap: 0;
text-align: center;
}
}
10 changes: 10 additions & 0 deletions projects/lib/pagination/styles/vars.scss
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
:root {
--wr-pagination-gap: 16px;
--wr-pagination-items-gap: 8px;
--wr-pagination-font-size: 14px;
--wr-pagination-total-color: var(--wr-color-medium);
--wr-pagination-ellipsis-color: var(--wr-color-medium);

--wr-pagination-item-size: 32px;
--wr-pagination-item-padding: 8px;
}
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 629a667

Please sign in to comment.