ElectronicBabylonianLiterature/ebl-frontend

View on GitHub
src/fragmentarium/ui/search/PaginationItems.tsx

Summary

Maintainability
A
1 hr
Test Coverage
A
100%
import React from 'react'
import { Pagination } from 'react-bootstrap'
import { parse, stringify } from 'query-string'
import { useHistory, useLocation } from 'react-router-dom'
import _ from 'lodash'

const NEIGHBOURING_PAGINATION_ITEMS = 3

function PaginationItem({
  paginationURLParam,
  active,
  index,
  setActivePage,
}: {
  paginationURLParam: string
  active: boolean
  index: number
  setActivePage: (number: number) => void
}) {
  const location = useLocation()
  const history = useHistory()
  return (
    <Pagination.Item
      active={active}
      onClick={(event) => {
        event.preventDefault()
        const query = parse(location.search, {
          parseNumbers: true,
        })
        setActivePage(index)
        query[paginationURLParam] = index
        history.push({
          search: stringify({ ...query }),
        })
      }}
    >
      {index + 1}
    </Pagination.Item>
  )
}

type PaginationItemElement = { component: () => JSX.Element; index: number }

function createItems(
  start,
  end,
  activePage,
  setActivePage: (number: number) => void,
  paginationURLParam: string
): readonly PaginationItemElement[] {
  return _.range(start, end + 1).map((index) => ({
    index: index,
    component: () => (
      <PaginationItem
        paginationURLParam={paginationURLParam}
        setActivePage={setActivePage}
        index={index}
        key={index}
        active={index === activePage}
      />
    ),
  }))
}

export default function PaginationItems({
  activePage,
  lastPage,
  setActivePage,
  paginationURLParam,
}: {
  activePage: number
  paginationURLParam: string
  lastPage: number
  setActivePage: (number: number) => void
}): JSX.Element {
  const start = Math.max(0, activePage - NEIGHBOURING_PAGINATION_ITEMS)
  const end = Math.min(activePage + NEIGHBOURING_PAGINATION_ITEMS, lastPage)
  const items = createItems(
    start,
    end,
    activePage,
    setActivePage,
    paginationURLParam
  )

  const generatePaginationItem = (index) => (
    <PaginationItem
      paginationURLParam={paginationURLParam}
      setActivePage={setActivePage}
      index={index}
      key={index}
      active={index === activePage}
    />
  )
  const first = generatePaginationItem(0)
  const second = generatePaginationItem(1)
  const nextToLast = generatePaginationItem(lastPage - 1)
  const last = generatePaginationItem(lastPage)
  const ellipsis1 = <Pagination.Ellipsis key={lastPage + 100} />
  const ellipsis2 = <Pagination.Ellipsis key={lastPage + 200} />

  let paginationItems: JSX.Element[] = []
  const mostLeft = items[0].index
  const mostRight = items[items.length - 1].index

  mostLeft > 0 && paginationItems.push(first)
  mostLeft === 2 && paginationItems.push(second)
  mostLeft > 2 && paginationItems.push(ellipsis1)

  paginationItems = [
    ...paginationItems,
    ...items.map((item) => item.component()),
  ]

  mostRight < lastPage - 2 && paginationItems.push(ellipsis2)
  mostRight === lastPage - 2 && paginationItems.push(nextToLast)
  mostRight < lastPage && paginationItems.push(last)

  return <Pagination>{paginationItems}</Pagination>
}