Source: search/Pagination.js

import { UiElement } from '../ui/UiElement.js';
import "./Pagination.css";

export { Pagination }

/**
 * A class creating a pagination with page *changed* events.
 * 
 * Events:
 * * changed
 * 
 * @author rkoppe <roland.koppe@awi.de>
 * @memberof vef.search
 */
class Pagination extends UiElement {

    options = {
        responsive: true,
        maxPages: 10 // in responsive mode the page count is ignored
    };

    // used for accurately calculating sizes for responsive mode
    widthMapping = [34, 40, 46, 52];

    /**
     * Initializes the pagination for the element defined by id and
     * options.
     * 
     * @param {string | HTMLElement} target 
     * @param {object} options 
     */
    constructor(target, options) {
        super(target, { 'changed': [] });

        this.setClass('vef-pagination');

        this.options = Object.assign(this.options, options);
    }

    getWidth_(text) {
        const index = text.toString().length - 1;

        if (index < this.widthMapping.length) {
            return this.widthMapping[index];
        }

        return this.widthMapping[this.widthMapping.length];
    }

    addButton_(text, page, currentPage) {
        const element = this.getElement();
        const a = document.createElement("a");

        const width = this.getWidth_(text);

        a.style.width = width + "px";
        a.innerText = text;
        a.href = "#";

        if (page == currentPage) {
            a.classList.add('active');
        } else {
            a.addEventListener("click", e => {
                e.preventDefault();
                this.fire('changed', page);
            });
        }

        element.appendChild(a);

        return width;
    }

    /**
     * @private
     */
    show_(page, pages, start, end) {
        this.setHtml('');
        this.addButton_('«', 1, page);
        for (let i = start; i <= end; ++i) {
            this.addButton_(i, i, page);
        }
        this.addButton_('»', pages, page);
    }

    /**
     * @private
     */
    responsiveShow_(page, pages) {
        const parentWidth = this.getElement().offsetWidth;

        // init with previous/next buttons and initial page
        const currentPageWidth = this.getWidth_(page);
        let width = 2 * this.widthMapping[0] + currentPageWidth;

        // simulate max size to dynamically adjust start position with an estimation size based on the current page
        let maxButtonCount = 0;
        let maxWidth = width;
        while (maxWidth < parentWidth) {
            if ((maxWidth + currentPageWidth) < parentWidth) {
                maxWidth += currentPageWidth;
                ++maxButtonCount;
            } else {
                break;
            }
        }

        const offset = Math.ceil(maxButtonCount * 0.5);
        let start = Math.max(1, page - offset);
        if ((page + offset) > pages) start = Math.max(1, page - maxButtonCount);
        let end = start;

        // calculate actual width
        let currentPage = start;
        while (width < parentWidth) {
            const itemWidth = this.getWidth_(++currentPage);
            if (((width + itemWidth) < parentWidth) && (currentPage <= pages)) {
                width += itemWidth;
                ++end;
            } else {
                break;
            }
        }

        this.show_(page, pages, start, end);
    }

    /**
     * Generates a new pagination for current page and number of pages.
     * 
     * @param {number} page 
     * @param {number} pages 
     */
    show(page, pages) {
        if (this.options.responsive) {
            this.responsiveShow_(page, pages);
        } else {
            let start = Math.max(1, page - Math.floor(this.options.maxPages / 2));
            let end = Math.min(start + this.options.maxPages - 1, pages);

            this.show_(page, pages, start, end);
        }
    }

    dispose() {
        super.dispose();
    }

}