Skip to content

Commit

Permalink
Implement pagination
Browse files Browse the repository at this point in the history
  • Loading branch information
qligier committed Nov 4, 2024
1 parent e983f0e commit 78cb62b
Show file tree
Hide file tree
Showing 6 changed files with 214 additions and 16 deletions.
1 change: 1 addition & 0 deletions src/scss/index.scss
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@
@use "header";
@use "form";
@use "logs";
@use "pagination";
@use "footer";
@use "notifications";

Expand Down
42 changes: 42 additions & 0 deletions src/scss/pagination.scss
Original file line number Diff line number Diff line change
@@ -0,0 +1,42 @@
@use 'colors';

#pagination-wrapper {
display: block;
margin: 4em auto;
padding: 2em 0;
text-align: center;
border-top: 1px solid colors.$oc-gray-4;
border-bottom: 1px solid colors.$oc-gray-4;

li {
display: inline-block;

&.page {
padding: 4px 10px;
margin: 0 7px;
border-radius: 5px;
}

&.page.clickable {
border: 1px solid colors.$oc-gray-2;
}

&.page.current {
border: 1px solid colors.$oc-blue-2;
background: colors.$oc-blue-2;
}

&.ellipsis {
margin: 0 20px;
color: colors.$oc-gray-5;
}

&.previous {
margin-right: 20px;
}

&.next {
margin-left: 20px;
}
}
}
7 changes: 5 additions & 2 deletions src/ts/app.ts
Original file line number Diff line number Diff line change
@@ -1,11 +1,14 @@
import {fetchIgBuildLogs, IgBuildLog} from "./api";
import {domNodeDataRefresh, rebuildLogsInDom} from "./dom";
import {buildPagination, domNodeDataRefresh, rebuildLogsInDom} from "./dom";
import {notifyError} from "./notification";
import {initRequestForm} from "./request-form";
import {Pagination} from "./pagination";

let allIgBuildLogs: IgBuildLog[] = [];
let fetchingData: boolean = false;

const pagination = new Pagination(50, rebuildLogsInDom, buildPagination);

const setFetchingData = (value: boolean) => {
fetchingData = value;
if (value) {
Expand All @@ -24,7 +27,7 @@ const refreshLogs: () => Promise<void> = async () => {
try {
allIgBuildLogs = await fetchIgBuildLogs();
// Sort and filter if necessary
rebuildLogsInDom(allIgBuildLogs);
pagination.logs = allIgBuildLogs;
} catch (e: unknown) {
if (e instanceof Error) {
notifyError('Failed to fetch logs', e);
Expand Down
69 changes: 55 additions & 14 deletions src/ts/dom.ts
Original file line number Diff line number Diff line change
Expand Up @@ -7,23 +7,23 @@ import {
mediumDateTimeFormatter,
timeFormatter
} from "./utils";

const domNodeLogWrapper: HTMLElement | null = document.getElementById("log-wrapper");
const domTemplateLogDay: HTMLElement | null = document.getElementById("log-day-template");
const domTemplateLog: HTMLElement | null = document.getElementById("log-template");
import {
Pagination,
PaginationCurrentPage,
PaginationEllipsis,
PaginationItem,
PaginationNextPage,
PaginationPage,
PaginationPreviousPage
} from "./pagination";

const domNodeLogWrapper: HTMLElement = document.getElementById("log-wrapper")!;
const domTemplateLogDay: HTMLElement = document.getElementById("log-day-template")!;
const domTemplateLog: HTMLElement = document.getElementById("log-template")!;
export const domNodeDataRefresh: HTMLElement = document.getElementById("refresh-data")!;
export const domTemplateNotification: HTMLTemplateElement = document.getElementById("notification-template") as HTMLTemplateElement;
export const domNodeNotifications: HTMLElement = document.getElementById("notifications")!;

if (!domNodeLogWrapper) {
throw new Error("Could not find the log wrapper element");
}
if (!domTemplateLogDay) {
throw new Error("Could not find the log-day template element");
}
if (!domTemplateLog) {
throw new Error("Could not find the log template element");
}
const domPaginationWrapper: HTMLElement = document.getElementById("pagination-wrapper")!;

document.querySelector("#shoebill-version")!.textContent = packageJson.version;

Expand Down Expand Up @@ -149,3 +149,44 @@ class CurrentDay {
this.logsWrapper.appendChild(fragment);
}
}

export const buildPagination = (items: PaginationItem[], pagination: Pagination): void => {
domPaginationWrapper.innerHTML = '';

for (const item of items) {
const li: HTMLLIElement = document.createElement('li');
if (item instanceof PaginationEllipsis) {
li.classList.add('ellipsis');
li.textContent = '…';
} else if (item instanceof PaginationPage) {
li.classList.add('page', 'clickable');
li.textContent = item.page.toString();
li.onclick = () => {
pagination.page = item.page;
window.scroll(0, 0);
};
li.setAttribute('title', `Go to page ${item.page}`);
} else if (item instanceof PaginationCurrentPage) {
li.classList.add('page', 'current');
li.textContent = item.page.toString();
li.setAttribute('title', 'Current page');
} else if (item instanceof PaginationPreviousPage) {
li.classList.add('page', 'previous', 'clickable');
li.textContent = 'Previous';
li.onclick = () => {
pagination.page = item.page;
window.scroll(0, 0);
};
li.setAttribute('title', `Go to page ${item.page}`);
} else if (item instanceof PaginationNextPage) {
li.classList.add('page', 'next', 'clickable');
li.textContent = 'Next';
li.onclick = () => {
pagination.page = item.page;
window.scroll(0, 0);
};
li.setAttribute('title', `Go to page ${item.page}`);
}
domPaginationWrapper.appendChild(li);
}
};
109 changes: 109 additions & 0 deletions src/ts/pagination.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,109 @@
import {IgBuildLog} from "./api";

export class Pagination {

private _currentPage: number = 1;
private numberOfPages: number = 0;

constructor(private readonly pageSize: number,
private readonly logRenderer: (logs: IgBuildLog[]) => void,
private readonly paginationRenderer: (item: PaginationItem[], pagination: Pagination) => void) {
}

private _logs: IgBuildLog[] = [];

set logs(logs: IgBuildLog[]) {
this._logs = logs;
this.numberOfPages = Math.ceil(logs.length / this.pageSize);
if (this._currentPage > this.numberOfPages) {
this.page = 1;
// The setter has called render() already
} else {
this.render();
}
}

set page(page: number) {
if (page < 1 || page > this.numberOfPages) {
return;
}
this._currentPage = page;
this.render();
}

render(): void {
const start = (this._currentPage - 1) * this.pageSize;
const end = start + this.pageSize;
this.logRenderer(this._logs.slice(start, end));

const nbPagesToShow = 4;

const items: PaginationItem[] = [];

// Show the first page and the 'previous' button
if (this._currentPage > 1) {
items.push(new PaginationPreviousPage(this._currentPage - 1));
items.push(new PaginationPage(1));
}
// If there are more than _nbPagesToShow_ pages, we need to show an ellipsis
if (this._currentPage > (nbPagesToShow + 2)) {
items.push(new PaginationEllipsis());
}

// Show the _nbPagesToShow_ pages before the current page
for (let i = Math.max(2, this._currentPage - nbPagesToShow); i <= this._currentPage - 1; i++) {
items.push(new PaginationPage(i));
}

// The current page
items.push(new PaginationCurrentPage(this._currentPage));

// Show the _nbPagesToShow_ pages after the current page
for (let i = this._currentPage + 1; i <= Math.min(this._currentPage + nbPagesToShow, this.numberOfPages - 1); i++) {
items.push(new PaginationPage(i));
}

// If there are more than _nbPagesToShow_ pages, we need to show an ellipsis
if (this._currentPage < (this.numberOfPages - nbPagesToShow - 2)) {
items.push(new PaginationEllipsis());
}

// Show the last page and the 'next' button
if (this._currentPage < this.numberOfPages) {
items.push(new PaginationPage(this.numberOfPages));
items.push(new PaginationNextPage(this._currentPage + 1));
}

this.paginationRenderer(items, this);
}
}

export class PaginationItem {
}

export class PaginationPreviousPage extends PaginationItem {
constructor(public readonly page: number) {
super();
}
}

export class PaginationNextPage extends PaginationItem {
constructor(public readonly page: number) {
super();
}
}

export class PaginationPage extends PaginationItem {
constructor(public readonly page: number) {
super();
}
}

export class PaginationCurrentPage extends PaginationItem {
constructor(public readonly page: number) {
super();
}
}

export class PaginationEllipsis extends PaginationItem {
}
2 changes: 2 additions & 0 deletions static/index.html
Original file line number Diff line number Diff line change
Expand Up @@ -68,6 +68,8 @@ <h3>Request the build of an Implementation Guide</h3>

<div id="log-wrapper"></div>

<ol id="pagination-wrapper"></ol>

<footer>
<p>
Built with 💙 in Switzerland by <a href="https://www.qligier.ch" rel="external noopener" target="_blank">Quentin Ligier</a>.
Expand Down

0 comments on commit 78cb62b

Please sign in to comment.