gd-bs
Version:
Bootstrap JavaScript, TypeScript and Web Components library.
206 lines (173 loc) • 7.41 kB
text/typescript
import { IPagination, IPaginationProps } from "./types";
import { Base } from "../base";
import { HTML, HTMLItem } from "./templates";
/**
* Pagination Alignment
*/
export enum PaginationAlignment {
Center = 1,
Left = 2,
Right = 3
}
/**
* Pagination
*/
class _Pagination extends Base<IPaginationProps> implements IPagination {
private _items: Array<HTMLLIElement> = null;
// Constructor
constructor(props: IPaginationProps, template: string = HTML, itemTemplate: string = HTMLItem) {
super(template, props);
// Configure the collapse
this.configure(itemTemplate);
// Configure the parent
this.configureParent();
}
// Configure the card group
private configure(itemTemplate: string) {
// Update the nav properties
this.props.label ? this.el.setAttribute("aria-label", this.props.label) : null;
// Update the list
let list = this.el.querySelector("ul");
if (list) {
this.props.isLarge ? list.classList.add("pagination-lg") : null;
this.props.isSmall ? list.classList.add("pagination-sm") : null;
// Read the alignment
switch (this.props.alignment) {
// Danger
case PaginationAlignment.Center:
list.classList.add("justify-content-center");
break;
// Dark
case PaginationAlignment.Right:
list.classList.add("justify-content-end");
break;
}
// Render the page numbers
this.renderPageNumbers(list, itemTemplate);
}
}
// Configures the next/previous buttons, based on the active index
private configureNextPrevButtons(activePage: number) {
// Update the previous button
let prevItem = this._items[0];
if (activePage == 1) {
// Ensure the previous item is disabled
prevItem.classList.add("disabled");
} else {
// Ensure the previous item is enabled
prevItem.classList.remove("disabled");
}
// Update the next button
let nextItem = this._items[this._items.length - 1];
if (activePage == this._items.length - 2) {
// Ensure the previous item is disabled
nextItem.classList.add("disabled");
} else {
// Ensure the previous item is enabled
nextItem.classList.remove("disabled");
}
}
// Configure the events
private configureEvents(item: HTMLLIElement) {
// See if this is the next or previous item and skip it
let link = item.querySelector("a").getAttribute("aria-label");
if (link == "Previous" || link == "Next") {
let isPrevious = link == "Previous";
// Add a click event
item.addEventListener("click", ev => {
// Prevent the page from moving to the top
ev.preventDefault();
// Do nothing if it's disabled
if (item.classList.contains("disabled")) { return; }
// Parse the items, excluding the next/previous items
for (let i = 1; i < this._items.length - 1; i++) {
let item = this._items[i];
// See if this item is active
if (item.classList.contains("active")) {
// See if the previous button was clicked
if (isPrevious) {
// Click the previous item if it's available
i - 1 > 0 ? this._items[i - 1].click() : null;
} else {
// Click the next item if it's available
i < this._items.length - 2 ? this._items[i + 1].click() : null;
}
// Break from the loop
break;
}
}
});
} else {
let pageNumber = parseInt(link);
// Add a click event
item.addEventListener("click", ev => {
// Prevent the page from moving to the top
ev.preventDefault();
// Parse the active items
let activeItems = this.el.querySelectorAll(".page-item.active");
for (let i = 0; i < activeItems.length; i++) {
let item = activeItems[i];
// Clear the active class
item.classList.remove("active");
// Remove the active span
let span = item.querySelector("span") as HTMLSpanElement;
span ? span.parentNode.removeChild(span) : null;
}
// Make this item active
item.classList.add("active");
// Add the span
let span = document.createElement("span");
span.classList.add("visually-hidden");
span.innerHTML = "(current)";
item.appendChild(span);
// Configure the next/previous buttons
this.configureNextPrevButtons(pageNumber);
// Class the click event
this.props.onClick ? this.props.onClick(pageNumber, ev) : null;
});
}
}
// Creates an page number item
private createItem(text: string, itemTemplate: string): HTMLLIElement {
// Create the item
let el = document.createElement("div");
el.innerHTML = itemTemplate;
let item = el.firstChild as HTMLLIElement;
this._items.push(item);
// Update the link
let link = item.querySelector("a");
if (link) {
link.innerHTML = text;
link.setAttribute("aria-label", link.innerHTML);
}
// Configure the events
this.configureEvents(item);
// Return the item
return item;
}
// Renders the page numbers
private renderPageNumbers(list: HTMLUListElement, itemTemplate: string) {
// Clear the items
this._items = [];
// Create the previous link
let item = this.createItem("Previous", itemTemplate);
item.classList.add("disabled");
item.classList.add("previous");
list.appendChild(item);
// Loop for the number of pages to create
// Parse the number of pages
let pages = this.props.numberOfPages || 1;
for (let i = 1; i <= pages; i++) {
// Create a link
item = this.createItem(i.toString(), itemTemplate);
i == 1 ? item.classList.add("active") : null;
list.appendChild(item);
}
// Create the next link
item = this.createItem("Next", itemTemplate);
pages > 1 ? null : item.classList.add("disabled");
item.classList.add("next");
list.appendChild(item);
}
}
export const Pagination = (props: IPaginationProps, template?: string, itemTemplate?: string): IPagination => { return new _Pagination(props, template, itemTemplate); }