OurBigBook
web/front/Pagination.tsx
import React from 'react'

import Maybe from 'front/Maybe'
import CustomLink from 'front/CustomLink'

export type PaginationPropsUrlFunc = (number) => string;

export interface PaginationProps {
  itemsCount: number;
  itemsPerPage: number;
  showPagesMax?: number;
  currentPage: number;
  urlFunc: PaginationPropsUrlFunc;
  what: string;
}

function PaginationItem(props) {
  const newProps = Object.assign({}, props)
  delete newProps.children
  delete newProps.className
  let className;
  if (props.className) {
    className = ' ' + props.className
  } else {
    className = ''
  }
  return <>
    <span className={`page-item${className}`} {...newProps}>
      <CustomLink href={props.href} className="page-link">{props.children}</CustomLink>
    </span>
    {' '}
  </>
}

export const getRange = (start, end) => {
  return [...Array(end - start + 1)].map((_, i) => start + i);
};


const Pagination = ({
  currentPage,
  itemsCount,
  itemsPerPage,
  showPagesMax,
  urlFunc,
  what,
}: PaginationProps) => {
  if (showPagesMax === undefined) {
    showPagesMax = 10
  }
  // - totalPages
  // - firstPage: 0-indexed
  // - lastPage: 0-indexed, inclusive
  const totalPages = Math.ceil(itemsCount / itemsPerPage)
  if (currentPage > totalPages) {
    currentPage = totalPages;
  }
  let firstPage = Math.max(0, currentPage - Math.floor(showPagesMax / 2));
  let lastPage = Math.min(totalPages - 1, currentPage + Math.floor(showPagesMax / 2));
  if (lastPage - firstPage + 1 < showPagesMax) {
    if (currentPage < totalPages / 2) {
      lastPage = Math.min(
        totalPages - 1,
        lastPage + (showPagesMax - (lastPage - firstPage))
      );
    } else {
      firstPage = Math.max(0, firstPage - (showPagesMax - (lastPage - firstPage)));
    }
  }
  if (lastPage - firstPage + 1 > showPagesMax) {
    if (currentPage > totalPages / 2) {
      firstPage = firstPage + 1;
    } else {
      lastPage = lastPage - 1;
    }
  }

  const pages = itemsCount > 0 ? getRange(firstPage, lastPage) : [];
  return (
    <nav>
      <div className="pagination">
        <Maybe test={totalPages > 1}>
          <span className="pages">
            <Maybe test={firstPage > 0}>
              <PaginationItem href={urlFunc(0)}>{`<<`}</PaginationItem>
            </Maybe>
            <Maybe test={currentPage > 0}>
              <PaginationItem href={urlFunc(currentPage)}>{`<`}</PaginationItem>
            </Maybe>
            {pages.map(page => {
              const isCurrent = page === currentPage;
              return (
                <PaginationItem
                  key={page.toString()}
                  className={isCurrent && "active"}
                  href={urlFunc(page + 1)}
                >
                  {page + 1}
                </PaginationItem>
              );
            })}
            <Maybe test={currentPage < totalPages - 1}>
              <PaginationItem  href={urlFunc(currentPage + 2)}>{`>`}</PaginationItem>
            </Maybe>
            <Maybe test={lastPage < totalPages - 1}>
              <PaginationItem href={urlFunc(totalPages)}>{`>>`}</PaginationItem>
            </Maybe>
          </span>
        </Maybe>
        <span className="total">
          Total {what}: <b>{itemsCount}</b>
        </span>
      </div>
    </nav>
  )
};

export default Pagination;