JAVASCRIPT

nextjs tailwind pagination 컴포넌트 (usePagination)

Neda 2023. 12. 26. 21:04

nextjs tailwind pagination 컴포넌트 (usePagination)

nextjs에서 tailwind를 사용하여 pagination 컴포넌트 만들기

 

usePagination 훅

// /lib/hooks/usePagination.ts

type NumberOrEllipsis = number | '...'
type NumberOrEllipsisArray = NumberOrEllipsis[]

const usePagination = ({
  currentPage,
  totalPages,
}: {
  currentPage: number
  totalPages: number
}): NumberOrEllipsisArray => {
  const pages: NumberOrEllipsisArray = [1]

  if (totalPages <= 5) {
    for (let i = 2; i < totalPages; i++) {
      pages.push(i)
    }
  } else if (currentPage <= 3) {
    pages.push(2, 3, '...')
  } else if (currentPage > totalPages - 3) {
    pages.push('...', totalPages - 2, totalPages - 1)
  } else {
    pages.push('...', currentPage, '...')
  }
  pages.push(totalPages)

  return pages
}

export default usePagination

 

Pagination 컴포넌트

 

'use client'
import React, { useCallback } from 'react'
import { Button } from './ui/button'
import {
  DotsThree,
  CaretLeft,
  CaretRight,
} from '@phosphor-icons/react/dist/ssr'
import { usePathname, useSearchParams } from 'next/navigation'
import Link from 'next/link'
import usePagination from '@/lib/hooks/usePagination'

interface PaginationProps {
  page: number
  totalPages: number
}

const Pagination = ({ page, totalPages }: PaginationProps) => {
  const pathname = usePathname()
  const searchParams = useSearchParams()
  const pages = usePagination({ currentPage: page, totalPages })

  // url 생성
  const getNewUrl = useCallback(
    (page: number) => {
      const params = new URLSearchParams(searchParams)
      params.set('page', page.toString())
      return `${pathname}?${params.toString()}`
    },
    [pathname, searchParams]
  )
  
  // 이전페이지, 다음페이지 버튼 링크
  const getNavButton = useCallback(
    (direction: 'left' | 'right') => {
      const isLeft = direction === 'left'
      const disabled = isLeft ? page <= 1 : page >= totalPages
      const Icon = isLeft ? CaretLeft : CaretRight
      const newPage = isLeft ? page - 1 : page + 1

      return (
        <Button
          variant='ghost'
          size='icon'
          disabled={disabled}
          asChild={!disabled}
        >
          {disabled ? (
            <Icon size={28} className='text-muted-foreground' />
          ) : (
            <Link href={getNewUrl(newPage)}>
              <Icon size={28} className='text-muted-foreground' />
            </Link>
          )}
        </Button>
      )
    },
    [getNewUrl, page, totalPages]
  )

  return (
    <div className='flex justify-center space-x-2 items-center'>
      {getNavButton('left')}
      {pages.map((p) => {
        if (p === '...') {
          return (
            <DotsThree key={p} size={30} className='text-muted-foreground' />
          )
        }
        return (
          <Button key={p} variant={p === page ? 'secondary' : 'ghost'} asChild>
            <Link href={getNewUrl(p)}>{p}</Link>
          </Button>
        )
      })}
      {getNavButton('right')}
    </div>
  )
}

export default Pagination