Skip to Content

Pagination

페이지 네비게이션을 표시하는 컴포넌트입니다.

개요

Pagination은 여러 페이지로 나뉜 콘텐츠를 탐색할 수 있도록 하는 네비게이션 컴포넌트입니다. Cals Pagination은 Cals 브랜드 스타일이 적용되어 있으며, 예약 목록 페이징과 고객 목록 페이징에 최적화되어 있습니다. Foundation Pagination의 모든 기능을 상속합니다.

주요 특징:

  • 숫자 페이지 버튼
  • 이전/다음 네비게이션
  • Cals 브랜드 컬러 적용 (Pink, Blue, Purple)
  • 현재 페이지 강조
  • 페이지 점프 기능
  • 반응형 디자인

사용 사례:

  • Cals 예약 목록 페이징
  • 고객 목록 페이징
  • 서비스 목록 페이징
  • 검색 결과 페이징

설치

npx @vortex/cli add pagination --package cals

기본 사용법

import "@vortex/ui-cals/theme"; // Cals 테마 적용 import { Pagination, PaginationContent, PaginationItem, PaginationLink, PaginationPrevious, PaginationNext, } from "@vortex/ui-cals"; export default function Example() { return ( <Pagination> <PaginationContent> <PaginationItem> <PaginationPrevious href="#" /> </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> ); }

Variants (변형)

Default

기본 페이지네이션 스타일입니다.

<Pagination> <PaginationContent> <PaginationItem> <PaginationPrevious href="#" /> </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>

With Ellipsis (생략 표시)

페이지가 많을 때 생략 표시를 사용합니다.

import { PaginationEllipsis } from "@vortex/ui-cals"; <Pagination> <PaginationContent> <PaginationItem> <PaginationPrevious href="#" /> </PaginationItem> <PaginationItem> <PaginationLink href="#">1</PaginationLink> </PaginationItem> <PaginationItem> <PaginationEllipsis /> </PaginationItem> <PaginationItem> <PaginationLink href="#" isActive> 5 </PaginationLink> </PaginationItem> <PaginationItem> <PaginationEllipsis /> </PaginationItem> <PaginationItem> <PaginationLink href="#">10</PaginationLink> </PaginationItem> <PaginationItem> <PaginationNext href="#" /> </PaginationItem> </PaginationContent> </Pagination>;

With Page Info

총 페이지 수와 현재 페이지 정보를 표시합니다.

<div className="flex items-center justify-between"> <p className="text-sm text-muted-foreground"> 총 <strong>120</strong>개 중 <strong>1-10</strong>개 표시 </p> <Pagination> <PaginationContent> <PaginationItem> <PaginationPrevious href="#" /> </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> </div>

With Page Size Selector

페이지당 항목 수를 선택할 수 있습니다.

import { Select, SelectTrigger, SelectValue, SelectContent, SelectItem, } from "@vortex/ui-cals"; <div className="flex items-center justify-between"> <div className="flex items-center gap-sm"> <span className="text-sm text-muted-foreground">페이지당</span> <Select defaultValue="10"> <SelectTrigger className="w-20"> <SelectValue /> </SelectTrigger> <SelectContent> <SelectItem value="10">10</SelectItem> <SelectItem value="20">20</SelectItem> <SelectItem value="50">50</SelectItem> <SelectItem value="100">100</SelectItem> </SelectContent> </Select> <span className="text-sm text-muted-foreground">개씩 보기</span> </div> <Pagination> <PaginationContent> <PaginationItem> <PaginationPrevious href="#" /> </PaginationItem> <PaginationItem> <PaginationLink href="#" isActive> 1 </PaginationLink> </PaginationItem> <PaginationItem> <PaginationLink href="#">2</PaginationLink> </PaginationItem> <PaginationItem> <PaginationNext href="#" /> </PaginationItem> </PaginationContent> </Pagination> </div>;

Cals 브랜딩

브랜드 컬러

Cals Pagination은 다음 브랜드 컬러를 사용합니다:

  • Primary (Pink): #e91e63 - 활성 페이지 강조
  • Secondary (Blue): #03a9f4 - 호버 상태
  • Accent (Purple): #9c27b0 - 특별 페이지 강조

Cals 특화 사용 가이드

예약 목록 페이징:

import { Pagination, PaginationContent, PaginationItem, PaginationLink, PaginationPrevious, PaginationNext, PaginationEllipsis } from '@vortex/ui-cals' import { Badge } from '@vortex/ui-cals' const currentPage = 5 const totalPages = 10 const totalReservations = 120 <div className="space-y-md"> <div className="flex items-center justify-between"> <div className="flex items-center gap-sm"> <span className="text-sm text-muted-foreground"> 총 <strong className="text-foreground">{totalReservations}</strong>개 예약 </span> <Badge variant="confirmed" size="sm">확정 45개</Badge> <Badge variant="pending" size="sm">대기 12개</Badge> </div> <span className="text-sm text-muted-foreground"> {(currentPage - 1) * 10 + 1}-{Math.min(currentPage * 10, totalReservations)}개 표시 </span> </div> <Pagination> <PaginationContent> <PaginationItem> <PaginationPrevious href="#" disabled={currentPage === 1} /> </PaginationItem> <PaginationItem> <PaginationLink href="#">1</PaginationLink> </PaginationItem> <PaginationItem> <PaginationEllipsis /> </PaginationItem> <PaginationItem> <PaginationLink href="#">{currentPage - 1}</PaginationLink> </PaginationItem> <PaginationItem> <PaginationLink href="#" isActive>{currentPage}</PaginationLink> </PaginationItem> <PaginationItem> <PaginationLink href="#">{currentPage + 1}</PaginationLink> </PaginationItem> <PaginationItem> <PaginationEllipsis /> </PaginationItem> <PaginationItem> <PaginationLink href="#">{totalPages}</PaginationLink> </PaginationItem> <PaginationItem> <PaginationNext href="#" disabled={currentPage === totalPages} /> </PaginationItem> </PaginationContent> </Pagination> </div>

고객 목록 페이징 (페이지 사이즈 선택 포함):

import { Pagination, PaginationContent, PaginationItem, PaginationLink, PaginationPrevious, PaginationNext, } from "@vortex/ui-cals"; import { Select, SelectTrigger, SelectValue, SelectContent, SelectItem, } from "@vortex/ui-cals"; <div className="space-y-md"> <div className="flex items-center justify-between"> <div className="flex items-center gap-sm"> <span className="text-sm text-muted-foreground">페이지당</span> <Select defaultValue="10"> <SelectTrigger className="w-20"> <SelectValue /> </SelectTrigger> <SelectContent> <SelectItem value="10">10</SelectItem> <SelectItem value="20">20</SelectItem> <SelectItem value="50">50</SelectItem> <SelectItem value="100">100</SelectItem> </SelectContent> </Select> <span className="text-sm text-muted-foreground">명씩 보기</span> </div> <p className="text-sm text-muted-foreground"> 총 <strong>350</strong>명 고객 </p> </div> <Pagination> <PaginationContent> <PaginationItem> <PaginationPrevious href="#" /> </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> </div>;

Foundation/iCignal/Cals 비교

속성FoundationiCignalCals
활성 색상중립 블루iCignal Blue (#2196f3)Cals Pink (#e91e63)
호버 색상중립 회색iCignal BlueCals Blue (#03a9f4)
브랜드 적용없음iCignal AnalyticsCals 예약 시스템
사용 맥락범용데이터 분석예약 관리
Badge 통합✅ 상태별 개수 표시
테마중립적 테마iCignal 테마Cals 테마 (@vortex/ui-cals/theme)

import 경로 차이:

// Foundation import { Pagination } from "@vortex/ui-foundation"; // iCignal import "@vortex/ui-icignal/theme"; import { Pagination } from "@vortex/ui-icignal"; // Cals import "@vortex/ui-cals/theme"; // 테마 import 필수 import { Pagination } from "@vortex/ui-cals";

Props API

PropTypeDefaultDescription
classNamestring-추가 CSS 클래스
childrenReactNode-페이지네이션 내용

PaginationLink Props:

PropTypeDefaultDescription
isActivebooleanfalse활성 페이지 여부
hrefstring-링크 URL
onClick() => void-클릭 이벤트 핸들러

PaginationPrevious/Next Props:

PropTypeDefaultDescription
disabledbooleanfalse비활성화 상태
hrefstring-링크 URL
onClick() => void-클릭 이벤트 핸들러

접근성

Pagination 컴포넌트는 WCAG 2.1 AA 기준을 준수합니다.

ARIA 속성:

  • aria-label="pagination" (네비게이션 역할 명시)
  • aria-current="page" (현재 페이지 표시)
  • aria-disabled (비활성 버튼)

키보드 네비게이션:

  • Tab: 페이지 버튼 간 이동
  • Enter: 페이지 선택

스크린 리더 지원:

  • 현재 페이지 명확하게 안내
  • 총 페이지 수 안내
  • 이전/다음 버튼 상태 안내

예제

예제 1: 예약 목록 페이징 ⭐

import "@vortex/ui-cals/theme"; import { Pagination, PaginationContent, PaginationItem, PaginationLink, PaginationPrevious, PaginationNext, PaginationEllipsis, } from "@vortex/ui-cals"; import { Badge, Button } from "@vortex/ui-cals"; import { Card, CardHeader, CardContent, CardFooter } from "@vortex/ui-cals"; import { Table, TableHeader, TableBody, TableRow, TableHead, TableCell, } from "@vortex/ui-cals"; import { Calendar, Download } from "lucide-react"; import { useState } from "react"; export default function ReservationListPagination() { const [currentPage, setCurrentPage] = useState(1); const totalPages = 12; const totalReservations = 120; const pageSize = 10; const statusCounts = { confirmed: 45, pending: 12, available: 30, cancelled: 8, completed: 25, }; const reservations = [ { id: "#001", customer: "홍길동", date: "2024-01-20", status: "confirmed" }, { id: "#002", customer: "김영희", date: "2024-01-21", status: "pending" }, { id: "#003", customer: "이철수", date: "2024-01-22", status: "available" }, // ... more reservations ]; const getPageNumbers = () => { const pages = []; if (totalPages <= 7) { for (let i = 1; i <= totalPages; i++) { pages.push(i); } } else { if (currentPage <= 4) { for (let i = 1; i <= 5; i++) pages.push(i); pages.push("ellipsis"); pages.push(totalPages); } else if (currentPage >= totalPages - 3) { pages.push(1); pages.push("ellipsis"); for (let i = totalPages - 4; i <= totalPages; i++) pages.push(i); } else { pages.push(1); pages.push("ellipsis"); for (let i = currentPage - 1; i <= currentPage + 1; i++) pages.push(i); pages.push("ellipsis"); pages.push(totalPages); } } return pages; }; return ( <Card> <CardHeader> <div className="flex items-center justify-between"> <div className="flex items-center gap-md"> <h2 className="text-xl font-bold">예약 목록</h2> <div className="flex items-center gap-xs"> <Badge variant="outline">전체 {totalReservations}</Badge> <Badge variant="confirmed" size="sm"> {statusCounts.confirmed} </Badge> <Badge variant="pending" size="sm"> {statusCounts.pending} </Badge> <Badge variant="available" size="sm"> {statusCounts.available} </Badge> </div> </div> <Button variant="outline"> <Download size={16} className="mr-xs" /> 엑셀 다운로드 </Button> </div> </CardHeader> <CardContent> <Table> <TableHeader> <TableRow> <TableHead>예약번호</TableHead> <TableHead>고객명</TableHead> <TableHead>예약일</TableHead> <TableHead>상태</TableHead> </TableRow> </TableHeader> <TableBody> {reservations.map((reservation) => ( <TableRow key={reservation.id} className={`bg-${reservation.status}/10`} > <TableCell>{reservation.id}</TableCell> <TableCell>{reservation.customer}</TableCell> <TableCell>{reservation.date}</TableCell> <TableCell> <Badge variant={reservation.status}> {reservation.status === "confirmed" && "예약 확정"} {reservation.status === "pending" && "승인 대기"} {reservation.status === "available" && "예약 가능"} </Badge> </TableCell> </TableRow> ))} </TableBody> </Table> </CardContent> <CardFooter> <div className="w-full space-y-md"> <div className="flex items-center justify-between text-sm text-muted-foreground"> <span> {(currentPage - 1) * pageSize + 1}- {Math.min(currentPage * pageSize, totalReservations)}개 표시 </span> <span> 총{" "} <strong className="text-foreground">{totalReservations}</strong>개 예약 </span> </div> <Pagination> <PaginationContent> <PaginationItem> <PaginationPrevious onClick={() => setCurrentPage((p) => Math.max(1, p - 1))} disabled={currentPage === 1} /> </PaginationItem> {getPageNumbers().map((page, index) => page === "ellipsis" ? ( <PaginationItem key={`ellipsis-${index}`}> <PaginationEllipsis /> </PaginationItem> ) : ( <PaginationItem key={page}> <PaginationLink onClick={() => setCurrentPage(page)} isActive={currentPage === page} > {page} </PaginationLink> </PaginationItem> ) )} <PaginationItem> <PaginationNext onClick={() => setCurrentPage((p) => Math.min(totalPages, p + 1)) } disabled={currentPage === totalPages} /> </PaginationItem> </PaginationContent> </Pagination> </div> </CardFooter> </Card> ); }

예제 2: 고객 목록 페이징 (페이지 사이즈 선택) ⭐

import "@vortex/ui-cals/theme"; import { Pagination, PaginationContent, PaginationItem, PaginationLink, PaginationPrevious, PaginationNext, } from "@vortex/ui-cals"; import { Select, SelectTrigger, SelectValue, SelectContent, SelectItem, } from "@vortex/ui-cals"; import { useState } from "react"; export default function CustomerListPagination() { const [currentPage, setCurrentPage] = useState(1); const [pageSize, setPageSize] = useState(10); const totalCustomers = 350; const totalPages = Math.ceil(totalCustomers / pageSize); return ( <div className="space-y-md"> <div className="flex items-center justify-between"> <div className="flex items-center gap-sm"> <span className="text-sm text-muted-foreground">페이지당</span> <Select value={pageSize.toString()} onValueChange={(v) => setPageSize(Number(v))} > <SelectTrigger className="w-20"> <SelectValue /> </SelectTrigger> <SelectContent> <SelectItem value="10">10</SelectItem> <SelectItem value="20">20</SelectItem> <SelectItem value="50">50</SelectItem> <SelectItem value="100">100</SelectItem> </SelectContent> </Select> <span className="text-sm text-muted-foreground">명씩 보기</span> </div> <div className="text-sm text-muted-foreground"> 총 <strong className="text-foreground">{totalCustomers}</strong>명 고객 |{" "} <strong className="text-foreground"> {(currentPage - 1) * pageSize + 1} </strong> - <strong className="text-foreground"> {Math.min(currentPage * pageSize, totalCustomers)} </strong>{" "} 표시 </div> </div> <Pagination> <PaginationContent> <PaginationItem> <PaginationPrevious onClick={() => setCurrentPage((p) => Math.max(1, p - 1))} disabled={currentPage === 1} /> </PaginationItem> {[...Array(Math.min(5, totalPages))].map((_, i) => { const pageNumber = i + 1; return ( <PaginationItem key={pageNumber}> <PaginationLink onClick={() => setCurrentPage(pageNumber)} isActive={currentPage === pageNumber} > {pageNumber} </PaginationLink> </PaginationItem> ); })} <PaginationItem> <PaginationNext onClick={() => setCurrentPage((p) => Math.min(totalPages, p + 1))} disabled={currentPage === totalPages} /> </PaginationItem> </PaginationContent> </Pagination> </div> ); }

관련 컴포넌트

Last updated on