Caroa UI

Pagination

ページネーションコンポーネント。ページ間のナビゲーションを提供します。

インポート

import {
  Pagination,
  PaginationContent,
  PaginationItem,
  PaginationLink,
  PaginationPrevious,
  PaginationNext,
  PaginationEllipsis,
  usePaginationRange,
  PAGINATION_DOTS,
} from '@caroainc/ui-components/client'

個別サブパス(バンドルサイズを抑えたい場合):

import {
  Pagination,
  PaginationContent,
  PaginationItem,
  PaginationLink,
  PaginationPrevious,
  PaginationNext,
  PaginationEllipsis,
  usePaginationRange,
  PAGINATION_DOTS,
} from '@caroainc/ui-components/pagination'

基本的な使い方

Loading...
<Pagination>
  <PaginationContent>
    <PaginationItem>
      <PaginationPrevious href="#" />
    </PaginationItem>
    <PaginationItem>
      <PaginationLink href="#">1</PaginationLink>
    </PaginationItem>
    <PaginationItem>
      <PaginationLink href="#" isActive>2</PaginationLink>
    </PaginationItem>
    <PaginationItem>
      <PaginationLink href="#">3</PaginationLink>
    </PaginationItem>
    <PaginationItem>
      <PaginationNext href="#" />
    </PaginationItem>
  </PaginationContent>
</Pagination>

省略記号付き

多数のページがある場合は PaginationEllipsis を使用します。この例は手書きで固定位置に省略記号を配置していますが、現在ページに応じて省略記号の位置を動的に計算したい場合は下記の usePaginationRange を使ってください。

Loading...
<Pagination>
  <PaginationContent>
    <PaginationItem>
      <PaginationPrevious href="#" />
    </PaginationItem>
    <PaginationItem>
      <PaginationLink href="#">1</PaginationLink>
    </PaginationItem>
    <PaginationItem>
      <PaginationLink href="#" isActive>2</PaginationLink>
    </PaginationItem>
    <PaginationItem>
      <PaginationLink href="#">3</PaginationLink>
    </PaginationItem>
    <PaginationItem>
      <PaginationEllipsis />
    </PaginationItem>
    <PaginationItem>
      <PaginationLink href="#">10</PaginationLink>
    </PaginationItem>
    <PaginationItem>
      <PaginationNext href="#" />
    </PaginationItem>
  </PaginationContent>
</Pagination>

usePaginationRangeフック(動的な省略記号)

usePaginationRange(current, total, siblingCount?) は、現在ページ・総ページ数から、先頭ページ・末尾ページ・現在ページ周辺(siblingCount件ずつ、デフォルト1)・省略記号(PAGINATION_DOTS)を含むページ番号レンジを計算して返します。手書きで省略記号の位置を管理する必要がなくなります。

Loading...
import {
  Pagination,
  PaginationContent,
  PaginationItem,
  PaginationLink,
  PaginationPrevious,
  PaginationNext,
  PaginationEllipsis,
  usePaginationRange,
  PAGINATION_DOTS,
} from '@caroainc/ui-components/client'
 
function PaginationRangeDemo() {
  const [page, setPage] = useState(1)
  const totalPages = 10
  const range = usePaginationRange(page, totalPages)
 
  return (
    <Pagination>
      <PaginationContent>
        <PaginationItem>
          <PaginationPrevious
            href="#"
            disabled={page <= 1}
            onClick={(e) => { e.preventDefault(); setPage((p) => Math.max(1, p - 1)) }}
          />
        </PaginationItem>
        {range.map((item, i) =>
          item === PAGINATION_DOTS ? (
            <PaginationItem key={`dots-${i}`}>
              <PaginationEllipsis />
            </PaginationItem>
          ) : (
            <PaginationItem key={item}>
              <PaginationLink
                href="#"
                isActive={item === page}
                onClick={(e) => { e.preventDefault(); setPage(item) }}
              >
                {item}
              </PaginationLink>
            </PaginationItem>
          )
        )}
        <PaginationItem>
          <PaginationNext
            href="#"
            disabled={page >= totalPages}
            onClick={(e) => { e.preventDefault(); setPage((p) => Math.min(totalPages, p + 1)) }}
          />
        </PaginationItem>
      </PaginationContent>
    </Pagination>
  )
}

ページサイズ選択と組み合わせる

Selectと組み合わせて、1ページあたりの件数を切り替えるUIを構成できます。

Loading...
import { Select, SelectContent, SelectItem, SelectTrigger, SelectValue } from '@caroainc/ui-components/client'
 
function PaginationWithPageSizeDemo() {
  const [page, setPage] = useState(1)
  const [pageSize, setPageSize] = useState('10')
 
  return (
    <div className="flex items-center justify-between gap-4">
      <div className="flex items-center gap-2 text-sm text-sub-foreground">
        <span>表示件数</span>
        <Select
          value={pageSize}
          onValueChange={(value) => { setPageSize(value); setPage(1) }}
        >
          <SelectTrigger className="w-20">
            <SelectValue />
          </SelectTrigger>
          <SelectContent>
            <SelectItem value="10">10</SelectItem>
            <SelectItem value="20">20</SelectItem>
            <SelectItem value="50">50</SelectItem>
          </SelectContent>
        </Select>
      </div>
      <Pagination>
        <PaginationContent>
          <PaginationItem>
            <PaginationPrevious
              href="#"
              onClick={(e) => { e.preventDefault(); setPage((p) => Math.max(1, p - 1)) }}
            />
          </PaginationItem>
          <PaginationItem>
            <PaginationLink href="#" isActive>{page}</PaginationLink>
          </PaginationItem>
          <PaginationItem>
            <PaginationNext
              href="#"
              onClick={(e) => { e.preventDefault(); setPage((p) => p + 1) }}
            />
          </PaginationItem>
        </PaginationContent>
      </Pagination>
    </div>
  )
}

asChildでのリンクコンポーネント差し替え(Next.js等)

PaginationLinkasChildに対応しています。SPAルーティング(Next.jsのLink等)に差し替える場合に使用してください(PaginationPrevious / PaginationNextは内部でアイコン+テキストの複数childrenを持つためasChildは使用できません。差し替えが必要な場合はPaginationLinkを直接使用してください)。

import Link from 'next/link'
import { PaginationLink, PaginationItem } from '@caroainc/ui-components/client'
 
<PaginationItem>
  <PaginationLink asChild isActive={page === 2}>
    <Link href="/items?page=2">2</Link>
  </PaginationLink>
</PaginationItem>

hrefを使わずクリックハンドラのみで制御するSPAパターンでは、hrefを省略してonClickだけを渡すこともできます(<a>のままhrefなしで描画されるため、キーボード操作を保つ場合はhref="#" + preventDefault()を推奨します)。

<PaginationLink onClick={() => setPage(2)} isActive={page === 2}>
  2
</PaginationLink>

サイズ

size プロパティでサイズを変更できます。

Loading...
{/* Small */}
<PaginationLink href="#" size="sm">1</PaginationLink>
 
{/* Default */}
<PaginationLink href="#" size="default">1</PaginationLink>
 
{/* Large */}
<PaginationLink href="#" size="lg">1</PaginationLink>

無効状態

最初や最後のページでは disabled を使用してナビゲーションを無効化します。

Loading...
<Pagination>
  <PaginationContent>
    <PaginationItem>
      <PaginationPrevious href="#" disabled />
    </PaginationItem>
    <PaginationItem>
      <PaginationLink href="#" isActive>1</PaginationLink>
    </PaginationItem>
    <PaginationItem>
      <PaginationLink href="#">2</PaginationLink>
    </PaginationItem>
    <PaginationItem>
      <PaginationLink href="#">3</PaginationLink>
    </PaginationItem>
    <PaginationItem>
      <PaginationNext href="#" />
    </PaginationItem>
  </PaginationContent>
</Pagination>

ページ番号のみ

前後ボタンなしでページ番号のみを表示することもできます。

Loading...
<Pagination>
  <PaginationContent>
    <PaginationItem>
      <PaginationLink href="#">1</PaginationLink>
    </PaginationItem>
    <PaginationItem>
      <PaginationLink href="#" isActive>2</PaginationLink>
    </PaginationItem>
    <PaginationItem>
      <PaginationLink href="#">3</PaginationLink>
    </PaginationItem>
    <PaginationItem>
      <PaginationLink href="#">4</PaginationLink>
    </PaginationItem>
    <PaginationItem>
      <PaginationLink href="#">5</PaginationLink>
    </PaginationItem>
  </PaginationContent>
</Pagination>

Props

PropTypeDefaultDescription
hrefstring-リンク先URL
isActivebooleanfalse現在のページかどうか
disabledbooleanfalse無効状態
size'default' | 'sm' | 'lg''default'サイズ
asChildbooleanfalse子要素をリンクとしてレンダリング(Next.jsのLink等への差し替え用)

PaginationPrevious / PaginationNext

PropTypeDefaultDescription
hrefstring-リンク先URL
disabledbooleanfalse無効状態
size'default' | 'sm' | 'lg''default'サイズ

asChildは使用できません(内部でアイコン+テキストの複数childrenをPaginationLinkへ渡しているため)。リンクコンポーネントを差し替えたい場合はPaginationLinkを直接使用してください。

usePaginationRange

PropTypeDefaultDescription
current*number-現在のページ番号(1始まり)
total*number-総ページ数
siblingCountnumber1現在ページの前後に表示するページ数

戻り値は (number | typeof PAGINATION_DOTS)[](例: [1, 'DOTS', 4, 5, 6, 'DOTS', 10])です。PAGINATION_DOTSをimportして比較に使用してください。