All files / app/src/shared/ui/pagination pagination.tsx

91.04% Statements 61/67
76.47% Branches 13/17
57.14% Functions 4/7
91.04% Lines 61/67

Press n or j to go to the next uncovered block, b, p or k for the previous block.

1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 951x                         1x 1x 1x 1x 1x 1x 1x 1x 1x   1x 1x   1x 1x 1x   1x   1x 6x 6x 6x 6x 6x   6x 6x 6x     1x 1x 1x 1x                 1x 1x     1x 2x 2x 2x 2x   2x 2x 2x 2x 2x 2x     1x 1x 1x 1x 1x 1x 1x 1x 32x 5x 5x 4x 1x 1x 1x 1x 1x 1x   1x  
import { Dispatch, FC, SetStateAction } from 'react'
import Arrow from '@/shared/assets/icons/common/arrow.svg'
import Dots from '@/shared/assets/icons/common/dots.svg'
import cn from 'classnames'
 
export interface PaginationProps {
  currentPage: number
  onChange: Dispatch<SetStateAction<number>>
  totalPageCount: number
  siblingCount?: number
  className?: string
}
 
export const Pagination: FC<PaginationProps> = ({
  currentPage,
  onChange,
  totalPageCount,
  siblingCount = 2,
  className,
}) => {
  const leftSiblingOffset = currentPage - 1 - siblingCount
  const rightSiblingOffset = totalPageCount - (currentPage + siblingCount)
 
  const leftSiblingCount = rightSiblingOffset > 0 ? siblingCount : siblingCount - rightSiblingOffset
  const rightSiblingCount = leftSiblingOffset > 0 ? siblingCount : siblingCount - leftSiblingOffset
 
  const shouldShowLeftDots = currentPage - siblingCount - 1 > 1 && currentPage - 1 - leftSiblingCount !== 1
  const shouldShowRightDots =
    currentPage + siblingCount + 1 < totalPageCount && currentPage + 1 + rightSiblingCount !== totalPageCount
 
  const pageDash = siblingCount * 2 + 1
 
  const renderButton = (page: number) => (
    <button key={page} className={cn('group pagination-button')} onClick={() => onChange(page)}>
      <p
        className={cn('text-base font-bold text-white group-active:text-main', {
          ['!text-main']: currentPage === page,
        })}
      >
        {page}
      </p>
    </button>
  )
 
  const renderDotsButton = (type: 'prev' | 'next') => (
    <button
      className='group pagination-button'
      onClick={() =>
        onChange(prev => {
          if (type === 'prev') {
            return prev - pageDash > 0 ? prev - pageDash : 1
          }
          return pageDash + currentPage < totalPageCount ? prev + pageDash : totalPageCount
        })
      }
    >
      <Dots className='fill-white w-[12px] h-[4px] group-active:fill-main' />
    </button>
  )
 
  const renderArrowButton = (type: 'prev' | 'next') => (
    <button
      className='border border-transparent group pagination-arrow bg-background-secondary active:border active:border-main disabled:border-background-primary disabled:bg-transparent'
      onClick={() => onChange(prev => (type === 'prev' ? --prev : ++prev))}
      disabled={(type === 'prev' && currentPage === 1) || (type === 'next' && currentPage >= totalPageCount)}
    >
      <Arrow
        className={`fill-white w-[7px] h-[13px] group-hover:fill-main group-disabled:fill-background-primary ${
          type === 'next' && 'rotate-180'
        }`}
      />
    </button>
  )
 
  return (
    <div className={`flex items-center justify-center flex-wrap gap-5 ${className}`}>
      {renderArrowButton('prev')}
      {renderButton(1)}
      {shouldShowLeftDots && renderDotsButton('prev')}
      {Array.from({ length: totalPageCount }, (_, i) => ++i)
        .filter(
          el =>
            el <= currentPage + rightSiblingCount &&
            el >= currentPage - leftSiblingCount &&
            el !== 1 &&
            el !== totalPageCount
        )
        .map(page => renderButton(page))}
      {shouldShowRightDots && renderDotsButton('next')}
      {totalPageCount > 1 && renderButton(totalPageCount)}
      {renderArrowButton('next')}
    </div>
  )
}