Skip to content

Commit

Permalink
feat: allow providing a Custom Pagination Component (#420)
Browse files Browse the repository at this point in the history
* feat: allow providing a Custom Pagination Component
  • Loading branch information
ghiscoding authored Oct 19, 2024
1 parent 78a0238 commit 58facc5
Show file tree
Hide file tree
Showing 14 changed files with 752 additions and 211 deletions.
File renamed without changes.
15 changes: 0 additions & 15 deletions favicon.svg

This file was deleted.

2 changes: 1 addition & 1 deletion index.html
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@
<meta charset="utf-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<meta http-equiv='X-UA-Compatible' content='ie=edge'>
<link rel="icon" type="image/svg+xml" href="favicon.svg" />
<link rel="icon" href="favicon.ico">
</head>

<body>
Expand Down
28 changes: 14 additions & 14 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -83,13 +83,13 @@
"/src/slickgrid-react"
],
"dependencies": {
"@slickgrid-universal/common": "~5.8.0",
"@slickgrid-universal/custom-footer-component": "~5.8.0",
"@slickgrid-universal/empty-warning-component": "~5.8.0",
"@slickgrid-universal/event-pub-sub": "~5.8.0",
"@slickgrid-universal/pagination-component": "~5.8.0",
"@slickgrid-universal/common": "~5.9.0",
"@slickgrid-universal/custom-footer-component": "~5.9.0",
"@slickgrid-universal/empty-warning-component": "~5.9.0",
"@slickgrid-universal/event-pub-sub": "~5.9.0",
"@slickgrid-universal/pagination-component": "~5.9.0",
"dequal": "^2.0.3",
"i18next": "^23.16.0",
"i18next": "^23.16.1",
"sortablejs": "^1.15.3"
},
"devDependencies": {
Expand All @@ -100,13 +100,13 @@
"@formkit/tempo": "^0.1.2",
"@popperjs/core": "^2.11.8",
"@release-it/conventional-changelog": "^9.0.0",
"@slickgrid-universal/composite-editor-component": "~5.8.0",
"@slickgrid-universal/custom-tooltip-plugin": "~5.8.0",
"@slickgrid-universal/excel-export": "~5.8.0",
"@slickgrid-universal/graphql": "~5.8.0",
"@slickgrid-universal/odata": "~5.8.0",
"@slickgrid-universal/rxjs-observable": "~5.8.0",
"@slickgrid-universal/text-export": "~5.8.0",
"@slickgrid-universal/composite-editor-component": "~5.9.0",
"@slickgrid-universal/custom-tooltip-plugin": "~5.9.0",
"@slickgrid-universal/excel-export": "~5.9.0",
"@slickgrid-universal/graphql": "~5.9.0",
"@slickgrid-universal/odata": "~5.9.0",
"@slickgrid-universal/rxjs-observable": "~5.9.0",
"@slickgrid-universal/text-export": "~5.9.0",
"@types/dompurify": "^3.0.5",
"@types/fnando__sparkline": "^0.3.7",
"@types/i18next-xhr-backend": "^1.4.2",
Expand All @@ -115,7 +115,7 @@
"@types/react-dom": "^18.3.1",
"@types/sortablejs": "^1.15.8",
"@types/text-encoding-utf-8": "^1.0.5",
"@vitejs/plugin-react": "^4.3.2",
"@vitejs/plugin-react": "^4.3.3",
"bootstrap": "^5.3.3",
"concurrently": "^9.0.1",
"copyfiles": "^2.4.1",
Expand Down
2 changes: 2 additions & 0 deletions src/examples/slickgrid/App.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -41,6 +41,7 @@ import Example38 from './Example38';
import Example39 from './Example39';
import Example40 from './Example40';
import Example41 from './Example41';
import Example42 from './Example42';

const routes: Array<{ path: string; route: string; component: any; title: string; }> = [
{ path: 'example1', route: '/example1', component: <Example1 />, title: '1- Basic Grid / 2 Grids' },
Expand Down Expand Up @@ -82,6 +83,7 @@ const routes: Array<{ path: string; route: string; component: any; title: string
{ path: 'example39', route: '/example39', component: <Example39 />, title: '39- Infinite Scroll with GraphQL' },
{ path: 'example40', route: '/example40', component: <Example40 />, title: '40- Infinite Scroll from JSON data' },
{ path: 'example41', route: '/example41', component: <Example41 />, title: '41- Drag & Drop' },
{ path: 'example42', route: '/example42', component: <Example42 />, title: '42- Custom Pagination' },
];

export default function Routes() {
Expand Down
2 changes: 1 addition & 1 deletion src/examples/slickgrid/Example24.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -579,7 +579,7 @@ class Example24 extends React.Component<Props, State> {

render() {
return !this.state.gridOptions ? '' : (
<div id="demo-container" className="container-fluid">
<div id="demo-container" className="container-fluid grid24">
<h2>
{this.title}
<span className="float-end font18">
Expand Down
67 changes: 67 additions & 0 deletions src/examples/slickgrid/Example42-Custom-Pager.scss
Original file line number Diff line number Diff line change
@@ -0,0 +1,67 @@
@use 'sass:color';

.custom-pagination {
display: flex;
justify-content: flex-end;
margin: 10px;
font-size: 13px;

.custom-pagination-settings {
display: inline-flex;
align-items: center;
margin-right: 30px;

.item-from,
.item-to,
.total-items {
margin: 0 4px;
}
}

.custom-pagination-nav {
display: flex;
align-items: center;
list-style-type: none;

.page-item {
display: flex;
width: 26px;
justify-content: center;
margin: 0;
&.disabled .pagination-link {
color: rgb(180, 179, 179);
background-color: rgb(180, 179, 179);
}
}

.page-number {
.page-number {
margin: 0 4px;
padding: 0 5px;
display: inline-flex;
justify-content: center;
width: 20px;
}
}

.page-count {
margin: 0 4px;
}

nav {
ul.custom-pagination-ul {
display: flex;
margin: 0;
padding: 0 5px;
color: #0d6efd;

.pagination-link {
color: #0d6efd;
&:hover {
color: color.adjust(#0d6efd, $lightness: 10%);
}
}
}
}
}
}
160 changes: 160 additions & 0 deletions src/examples/slickgrid/Example42-Custom-Pager.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,160 @@
import type { BasePaginationComponent, PaginationMetadata, PaginationService, PubSubService, SlickGrid, Subscription } from '@slickgrid-universal/common';
import React from 'react';

import './Example42-Custom-Pager.scss';

interface Props { }
interface State {
currentPagination: PaginationMetadata;
isLeftPaginationDisabled: boolean;
isRightPaginationDisabled: boolean;
}

/** Custom Pagination Componnet, please note that you MUST `implements BasePaginationComponent` with required functions */
export class CustomPagerComponent extends React.Component<Props, State> implements BasePaginationComponent {
protected _elm?: HTMLDivElement | null;
protected _grid!: SlickGrid;
protected _paginationElement!: HTMLDivElement;
protected _paginationService!: PaginationService;
protected _pubSubService!: PubSubService;
protected _subscriptions: Subscription[] = [];

constructor(public readonly props: Props) {
super(props);
this.state = {
currentPagination: {} as PaginationMetadata,
isLeftPaginationDisabled: false,
isRightPaginationDisabled: false
};
}

init(grid: SlickGrid, paginationService: PaginationService, pubSubService: PubSubService) {
this._grid = grid;
this._paginationService = paginationService;
this._pubSubService = pubSubService;
const currentPagination = this._paginationService.getFullPagination();
this.setState((props: Props, state: any) => {
return {
...state,
currentPagination,
isLeftPaginationDisabled: this.checkLeftPaginationDisabled(currentPagination),
isRightPaginationDisabled: this.checkRightPaginationDisabled(currentPagination)
}
});

// Anytime the pagination is initialized or has changes,
// we'll copy the data into a local object so that we can add binding to this local object
this._subscriptions.push(
this._pubSubService.subscribe<PaginationMetadata>('onPaginationRefreshed', paginationChanges => {
this.setState((props: Props, state: any) => {
return {
...state,
currentPagination: paginationChanges,
isLeftPaginationDisabled: this.checkLeftPaginationDisabled(paginationChanges),
isRightPaginationDisabled: this.checkRightPaginationDisabled(paginationChanges)
}
});
})
);
}

/**
* dispose (unmount), please note that `componentWillUnmount()` will NOT be called and `dispose()` is the only one that will be called
* (because Slickgrid-Universal only deals with `dispose()` and also to avoid conflicts between the 2 methods)
*/
dispose() {
this._pubSubService.unsubscribeAll(this._subscriptions);
this._paginationElement.remove();
}

renderPagination() {
this._paginationElement = this._elm as HTMLDivElement;
this._paginationElement.id = 'pager';
this._paginationElement.className = `custom-pagination pager ${this._grid.getUID()}`;
this._paginationElement.style.width = '100%';
}

onFirstPageClicked(event: any): void {
if (!this.checkLeftPaginationDisabled(this.state.currentPagination)) {
this._paginationService.goToFirstPage(event);
}
}

onLastPageClicked(event: any): void {
if (!this.checkRightPaginationDisabled(this.state.currentPagination)) {
this._paginationService.goToLastPage(event);
}
}

onNextPageClicked(event: any): void {
if (!this.checkRightPaginationDisabled(this.state.currentPagination)) {
this._paginationService.goToNextPage(event);
}
}

onPreviousPageClicked(event: any): void {
if (!this.checkLeftPaginationDisabled(this.state.currentPagination)) {
this._paginationService.goToPreviousPage(event);
}
}

protected checkLeftPaginationDisabled(currentPagination: PaginationMetadata): boolean {
return currentPagination.pageNumber === 1 || currentPagination.totalItems === 0;
}

protected checkRightPaginationDisabled(currentPagination: PaginationMetadata): boolean {
return currentPagination.pageNumber === currentPagination.pageCount || currentPagination.totalItems === 0;
}

render() {
return (
<div className="custom-pagination" ref={elm => this._elm = elm}>
<span className="custom-pagination-settings">
<span className="custom-pagination-count">
<span className="page-info-from-to">
<span className="item-from" aria-label="Page Item From" data-test="item-from">
{this.state.currentPagination.dataFrom}
</span>-
<span className="item-to" aria-label="Page Item To" data-test="item-to">
{this.state.currentPagination.dataTo}
</span>
of
</span>
<span className="page-info-total-items">
<span className="total-items" aria-label="Total Items" data-test="total-items">{this.state.currentPagination.totalItems}</span>
<span className="text-items"> items</span>
</span>
</span>
</span>
<div className="custom-pagination-nav">
<nav aria-label="Page navigation">
<ul className="custom-pagination-ul">
<li className={'li page-item seek-first' + (this.state.isLeftPaginationDisabled ? ' disabled' : '')}>
<a className="pagination-link mdi mdi-page-first icon-seek-first mdi-22px" aria-label="First Page" role="button" onClick={$event => this.onFirstPageClicked($event)}></a>
</li>
<li className={'li page-item seek-prev' + (this.state.isLeftPaginationDisabled ? ' disabled' : '')}>
<a className="pagination-link icon-seek-prev mdi mdi-chevron-down mdi-22px mdi-rotate-90" aria-label="Previous Page" role="button" onClick={$event => this.onPreviousPageClicked($event)}></a>
</li>
</ul>
</nav>
<div className="page-number">
<span className="text-page">Page</span>
<span className="page-number" aria-label="Page Number" data-test="page-number-label">{this.state.currentPagination.pageNumber}</span>
of
<span className="page-count" data-test="page-count">{this.state.currentPagination.pageCount}</span>
</div>
<nav aria-label="Page navigation">
<ul className="custom-pagination-ul">
<li className={'li page-item seek-next' + (this.state.isRightPaginationDisabled ? ' disabled' : '')} onClick={$event => this.onNextPageClicked($event)}>
<a className="pagination-link icon-seek-next mdi mdi-chevron-down mdi-22px mdi-rotate-270" aria-label="Next Page" role="button" ></a>
</li>
<li className={'li page-item seek-end' + (this.state.isRightPaginationDisabled ? ' disabled' : '')}>
<a className="pagination-link icon-seek-end mdi mdi-page-last mdi-22px" aria-label="Last Page" role="button" onClick={$event => this.onLastPageClicked($event)}></a>
</li>
</ul>
</nav>
</div>
</div>
);
}
}
Loading

0 comments on commit 58facc5

Please sign in to comment.