pagination.tsx 2.9 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133
  1. import * as React from 'react';
  2. import { cn } from '@/lib/utils';
  3. import { Button } from '@/components/ui/button';
  4. import {
  5. ChevronLeftIcon,
  6. ChevronRightIcon,
  7. MoreHorizontalIcon,
  8. } from 'lucide-react';
  9. function Pagination({ className, ...props }: React.ComponentProps<'nav'>) {
  10. return (
  11. <nav
  12. role="navigation"
  13. aria-label="pagination"
  14. data-slot="pagination"
  15. className={cn('mx-auto flex w-full justify-center', className)}
  16. {...props}
  17. />
  18. );
  19. }
  20. function PaginationContent({
  21. className,
  22. ...props
  23. }: React.ComponentProps<'ul'>) {
  24. return (
  25. <ul
  26. data-slot="pagination-content"
  27. className={cn('gap-0.5 flex items-center', className)}
  28. {...props}
  29. />
  30. );
  31. }
  32. function PaginationItem({ ...props }: React.ComponentProps<'li'>) {
  33. return <li data-slot="pagination-item" {...props} />;
  34. }
  35. type PaginationLinkProps = {
  36. isActive?: boolean;
  37. } & Pick<React.ComponentProps<typeof Button>, 'size'> &
  38. React.ComponentProps<'a'>;
  39. function PaginationLink({
  40. className,
  41. isActive,
  42. size = 'icon',
  43. ...props
  44. }: PaginationLinkProps) {
  45. return (
  46. <Button
  47. variant={isActive ? 'outline' : 'ghost'}
  48. size={size}
  49. className={cn(className)}
  50. nativeButton={false}
  51. render={
  52. <a
  53. aria-current={isActive ? 'page' : undefined}
  54. data-slot="pagination-link"
  55. data-active={isActive}
  56. {...props}
  57. />
  58. }
  59. />
  60. );
  61. }
  62. function PaginationPrevious({
  63. className,
  64. text = 'Previous',
  65. ...props
  66. }: React.ComponentProps<typeof PaginationLink> & { text?: string }) {
  67. return (
  68. <PaginationLink
  69. aria-label="Go to previous page"
  70. size="default"
  71. className={cn('pl-1.5!', className)}
  72. {...props}
  73. >
  74. <ChevronLeftIcon data-icon="inline-start" className="cn-rtl-flip" />
  75. <span className="hidden sm:block">{text}</span>
  76. </PaginationLink>
  77. );
  78. }
  79. function PaginationNext({
  80. className,
  81. text = 'Next',
  82. ...props
  83. }: React.ComponentProps<typeof PaginationLink> & { text?: string }) {
  84. return (
  85. <PaginationLink
  86. aria-label="Go to next page"
  87. size="default"
  88. className={cn('pr-1.5!', className)}
  89. {...props}
  90. >
  91. <span className="hidden sm:block">{text}</span>
  92. <ChevronRightIcon data-icon="inline-end" className="cn-rtl-flip" />
  93. </PaginationLink>
  94. );
  95. }
  96. function PaginationEllipsis({
  97. className,
  98. ...props
  99. }: React.ComponentProps<'span'>) {
  100. return (
  101. <span
  102. aria-hidden
  103. data-slot="pagination-ellipsis"
  104. className={cn(
  105. "size-8 [&_svg:not([class*='size-'])]:size-4 flex items-center justify-center",
  106. className,
  107. )}
  108. {...props}
  109. >
  110. <MoreHorizontalIcon />
  111. <span className="sr-only">More pages</span>
  112. </span>
  113. );
  114. }
  115. export {
  116. Pagination,
  117. PaginationContent,
  118. PaginationEllipsis,
  119. PaginationItem,
  120. PaginationLink,
  121. PaginationNext,
  122. PaginationPrevious,
  123. };