ぼざクリタグ広場 https://hub.nizika.monster
You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
 
 
 
 
 
 

80 lines
2.0 KiB

  1. import { Link, useLocation } from 'react-router-dom'
  2. import type { FC } from 'react'
  3. type Props = { page: number
  4. totalPages: number
  5. siblingCount?: number }
  6. const range = (start: number, end: number): number[] =>
  7. [...Array (end - start + 1).keys ()].map (i => start + i)
  8. const getPages = (
  9. page: number,
  10. total: number,
  11. siblingCount: number,
  12. ): (number | '…')[] => {
  13. if (total <= 1)
  14. return [1]
  15. const first = 1
  16. const last = total
  17. const left = Math.max (page - siblingCount, first)
  18. const right = Math.min (page + siblingCount, last)
  19. const pages: (number | '…')[] = []
  20. pages.push (first)
  21. if (left > first + 1)
  22. pages.push ('…')
  23. const midStart = Math.max (left, first + 1)
  24. const midEnd = Math.min (right, last - 1)
  25. pages.push (...range (midStart, midEnd))
  26. if (right < last - 1)
  27. pages.push ('…')
  28. if (last !== first)
  29. pages.push (last)
  30. return pages.filter ((v, i, arr) => i === 0 || v !== arr[i - 1])
  31. }
  32. export default (({ page, totalPages, siblingCount = 4 }) => {
  33. const location = useLocation ()
  34. const buildTo = (p: number) => {
  35. const qs = new URLSearchParams (location.search)
  36. qs.set ('page', String (p))
  37. return `${ location.pathname }?${ qs.toString () }`
  38. }
  39. const pages = getPages (page, totalPages, siblingCount)
  40. return (
  41. <nav className="mt-4 flex justify-center" aria-label="Pagination">
  42. <div className="flex items-center gap-2">
  43. {(page > 1)
  44. ? <Link to={buildTo (page - 1)} aria-label="前のページ">&lt;</Link>
  45. : <span aria-hidden>&lt;</span>}
  46. {pages.map ((p, idx) => (
  47. (p === '…')
  48. ? <span key={`dots-${ idx }`}>…</span>
  49. : ((p === page)
  50. ? <span key={p} className="font-bold" aria-current="page">{p}</span>
  51. : <Link key={p} to={buildTo (p)}>{p}</Link>)))}
  52. {(page < totalPages)
  53. ? <Link to={buildTo (page + 1)} aria-label="次のページ">&gt;</Link>
  54. : <span aria-hidden>&gt;</span>}
  55. </div>
  56. </nav>)
  57. }) satisfies FC<Props>