省略メニュー付きページネーションの実装

省略メニュー付きページネーションの実装

こんにちは!@Ryo54388667です!☺️

普段は都内でエンジニアとして業務をしてます!

主にTypeScriptやNext.jsといった技術を触っています。今回は省略メニュー付きページネーションの実装方法を紹介していきます!


実装するもの

#

まずは実装するものを見ていただくのが良いのかなと思います。

これは本ブログのトップページで実装済みです。こちらのStorybookのほうが見やすいかもしれません。

様々なブログでも実装されているUIですので、Webを探せば実装方法はたくさん紹介されています。ただ、このような省略メニュー付きのページネーションは珍しいのかなと思い、本記事を書き起こしました。



実装の詳細

#

コードだけ教えてくれ!というかたは、下記のファイルをご覧ください!


詳細を説明していきます。


const rate = totalCount / PER_PAGE; const totalPages = rate < MINIMUM_PAGE_COUNT ? MINIMUM_PAGE_COUNT : Math.ceil(rate);

ここで、rateは総アイテム数を1ページあたりのアイテム数で割った値です。totalPagesは、rateが1未満の場合は1、そうでなければrateを切り上げた値です。ここでは総ページ数を計算しています。例えば 20(記事数) ÷ 4 (記事数 / page) = 5 (page) となります。



次に、前後のページ範囲を計算します。smallNumRangeは現在のページより前のページ番号のリスト、largeNumRangeは現在のページより後のページ番号のリストです。

const previousPageRange = useMemo(() => Array.from({ length: totalPages + INDEX_OFFSET }) .map((_, index) => index + INDEX_OFFSET) .slice(SMALL_RANGE_START_OFFSET, currentPage - SMALL_RANGE_END_OFFSET), [currentPage, totalPages]) const nextPageRange = useMemo(() => Array.from({ length: totalPages + INDEX_OFFSET }) .map((_, index) => index + INDEX_OFFSET) .slice(currentPage + LARGE_RANGE_START_OFFSET, totalPages - LARGE_RANGE_END_OFFSET), [currentPage, totalPages])

なぜ SMALL_RANGE_END_OFFSET, LARGE_RANGE_END_OFFSET が必要なのか?

#

previousPageRange とnextPageRangeの計算において、sliceメソッドで -1 (SMALL_RANGE_END_OFFSET)を使用しています。これには以下の理由があります。

slice(SMALL_RANGE_START_OFFSET, currentPage - SMALL_RANGE_END_OFFSET) の currentPage - SMALL_RANGE_END_OFFSET は、現在のページの1つ前までのページを取得するためです。例えば、現在のページが5の場合、previousPageRangeは 2, 3 になります。(Storybookでいうとココです!)

slice(currentPage + LARGE_RANGE_START_OFFSET, totalPages - LARGE_RANGE_END_OFFSET) は、現在のページの次のページから最後のページの1つ前までのページを取得するためです。例えば、現在のページが5で、総ページ数が10の場合、nextPageRangeは 7, 8, 9 になります。


最初のページボタン

#

最初のページ(ページ番号1)

<li> <PaginationItem pageNumber={1} currentPage={currentPage}>1</PaginationItem> </li>

前のページボタン

#

現在のページが1でない場合、前のページに移動するボタン

<li> <PaginationItem pageNumber={currentPage - 1} currentPage={currentPage}> <svg className="-scale-x-90" xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24" fill="none" stroke="currentColor" strokeWidth="2" strokeLinecap="round" strokeLinejoin="round" > <path d="m9 18 6-6-6-6" /> </svg> </PaginationItem> </li>

省略記号と小さいページ範囲のMenu

#

現在のページが4以上の場合、省略記号と小さいページ範囲のMenuを表示します。

{currentPage >= 4 && ( <li className="relative group mx-6"> <button type="button"> <span>...</span> </button> <EllipsisMenu range={smallNumRange} /> </li> )}

EllipsisMenuについては下記のファイルを参照してください。


前のページボタン

#

現在のページが3以上の場合、前のページを表示。

{currentPage >= 3 && ( <li> <PaginationItem pageNumber={currentPage - 1} currentPage={currentPage}> {currentPage - 1} </PaginationItem> </li> )}

現在のページボタン

#

現在のページが最初と最後のページでない場合、現在のページを表示。

{currentPage !== 1 && currentPage !== totalPages && ( <li> <PaginationItem pageNumber={currentPage} currentPage={currentPage}> {currentPage} </PaginationItem> </li> )}

次のページボタン

#

現在のページが最後のページでない場合、次のページを表示。

{currentPage < totalPages && currentPage + 1 !== totalPages && ( <li> <PaginationItem pageNumber={currentPage + 1} currentPage={currentPage}> {currentPage + 1} </PaginationItem> </li> )}

省略記号と大きなページ範囲のMenu

#

現在のページが最後のページの3ページ前以下の場合、省略記号とその後のページ範囲を表示。

{currentPage <= totalPages - 3 && ( <li className="relative group mx-6"> <button type="button"> <span>...</span> </button> <EllipsisMenu range={largeNumRange} /> </li> )}

最後のページボタン

#

総ページ数が1でない場合、最後のページを表示します。

{totalPages !== 1 && ( <li> <PaginationItem pageNumber={totalPages} currentPage={currentPage}> {totalPages} </PaginationItem> </li> )}

次のページボタン

#

現在のページが最後のページでない場合、次のページに移動するボタンを表示。

{currentPage !== totalPages && ( <li> <PaginationItem pageNumber={currentPage + 1} currentPage={currentPage}> <svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24" fill="none" stroke="currentColor" strokeWidth="2" strokeLinecap="round" strokeLinejoin="round" > <path d="m9 18 6-6-6-6" /> </svg> </PaginationItem> </li> )}

以上です!より良い方法があれば教えてください〜

最後まで読んでいただきありがとうございます!

気ままにつぶやいているので、気軽にフォローをお願いします!🥺





GitHub
修正をリクエストする