@@ -9,13 +9,59 @@ import {
99 PaginationNext ,
1010 PaginationPrevious ,
1111} from "@/components/ui/pagination" ;
12+ import { ArrowUpRightIcon } from "lucide-react" ;
13+ import { useState } from "react" ;
14+ import { cn } from "../lib/utils" ;
15+ import { Button } from "./ui/button" ;
16+ import { Input } from "./ui/input" ;
1217
1318export const PaginationButtons = ( props : {
1419 activePage : number ;
1520 totalPages : number ;
1621 onPageClick : ( page : number ) => void ;
1722} ) => {
1823 const { activePage, totalPages, onPageClick : setPage } = props ;
24+ const [ inputHasError , setInputHasError ] = useState ( false ) ;
25+ const [ pageNumberInput , setPageNumberInput ] = useState ( "" ) ;
26+
27+ if ( totalPages === 1 ) {
28+ return null ;
29+ }
30+
31+ function handlePageInputSubmit ( ) {
32+ const page = Number ( pageNumberInput ) ;
33+
34+ setInputHasError ( false ) ;
35+ if ( Number . isInteger ( page ) && page > 0 && page <= totalPages ) {
36+ setPage ( page ) ;
37+ setPageNumberInput ( "" ) ;
38+ } else {
39+ setInputHasError ( true ) ;
40+ }
41+ }
42+
43+ // just render all the page buttons directly
44+ if ( totalPages <= 6 ) {
45+ const pages = [ ...Array ( totalPages ) ] . map ( ( _ , i ) => i + 1 ) ;
46+ return (
47+ < Pagination >
48+ < PaginationContent >
49+ { pages . map ( ( page ) => (
50+ < PaginationItem key = { page } >
51+ < PaginationLink
52+ isActive = { activePage === page }
53+ onClick = { ( ) => {
54+ setPage ( page ) ;
55+ } }
56+ >
57+ { page }
58+ </ PaginationLink >
59+ </ PaginationItem >
60+ ) ) }
61+ </ PaginationContent >
62+ </ Pagination >
63+ ) ;
64+ }
1965
2066 return (
2167 < Pagination >
@@ -28,24 +74,28 @@ export const PaginationButtons = (props: {
2874 } }
2975 />
3076 </ PaginationItem >
77+
78+ { /* First page */ }
3179 { activePage - 3 > 0 && (
32- < PaginationItem className = "max-sm:hidden" >
33- < PaginationEllipsis />
34- </ PaginationItem >
35- ) }
36- { activePage - 2 > 0 && (
37- < PaginationItem className = "max-sm:hidden" >
80+ < PaginationItem >
3881 < PaginationLink
3982 onClick = { ( ) => {
40- setPage ( activePage - 2 ) ;
83+ setPage ( 1 ) ;
4184 } }
4285 >
43- { activePage - 2 }
86+ 1
4487 </ PaginationLink >
4588 </ PaginationItem >
4689 ) }
47- { activePage - 1 > 0 && (
90+
91+ { activePage - 3 > 0 && (
4892 < PaginationItem >
93+ < PaginationEllipsis className = "max-sm:w-3" />
94+ </ PaginationItem >
95+ ) }
96+
97+ { activePage - 1 > 0 && (
98+ < PaginationItem className = "max-sm:hidden" >
4999 < PaginationLink
50100 onClick = { ( ) => {
51101 setPage ( activePage - 1 ) ;
@@ -55,11 +105,13 @@ export const PaginationButtons = (props: {
55105 </ PaginationLink >
56106 </ PaginationItem >
57107 ) }
108+
58109 < PaginationItem >
59110 < PaginationLink isActive > { activePage } </ PaginationLink >
60111 </ PaginationItem >
112+
61113 { activePage + 1 <= totalPages && (
62- < PaginationItem >
114+ < PaginationItem className = "max-sm:hidden" >
63115 < PaginationLink
64116 onClick = { ( ) => {
65117 setPage ( activePage + 1 ) ;
@@ -69,22 +121,26 @@ export const PaginationButtons = (props: {
69121 </ PaginationLink >
70122 </ PaginationItem >
71123 ) }
72- { activePage + 2 <= totalPages && (
73- < PaginationItem className = "max-sm:hidden" >
124+
125+ { activePage + 3 <= totalPages && (
126+ < PaginationItem >
127+ < PaginationEllipsis className = "max-sm:w-3" />
128+ </ PaginationItem >
129+ ) }
130+
131+ { /* Last page */ }
132+ { activePage + 3 <= totalPages && (
133+ < PaginationItem >
74134 < PaginationLink
75135 onClick = { ( ) => {
76- setPage ( activePage + 2 ) ;
136+ setPage ( totalPages ) ;
77137 } }
78138 >
79- { activePage + 2 }
139+ { totalPages }
80140 </ PaginationLink >
81141 </ PaginationItem >
82142 ) }
83- { activePage + 3 <= totalPages && (
84- < PaginationItem className = "max-sm:hidden" >
85- < PaginationEllipsis />
86- </ PaginationItem >
87- ) }
143+
88144 < PaginationItem >
89145 < PaginationNext
90146 disabled = { activePage === totalPages }
@@ -93,6 +149,34 @@ export const PaginationButtons = (props: {
93149 } }
94150 />
95151 </ PaginationItem >
152+
153+ < div className = "relative flex items-center" >
154+ < Input
155+ value = { pageNumberInput }
156+ onChange = { ( e ) => {
157+ setInputHasError ( false ) ;
158+ setPageNumberInput ( e . target . value ) ;
159+ } }
160+ type = "number"
161+ placeholder = "Page"
162+ className = { cn (
163+ "w-[60px] bg-transparent [appearance:textfield] max-sm:placeholder:text-sm lg:w-[100px] lg:pr-8 [&::-webkit-inner-spin-button]:appearance-none [&::-webkit-outer-spin-button]:appearance-none" ,
164+ inputHasError && "text-red-500" ,
165+ ) }
166+ onKeyDown = { ( e ) => {
167+ if ( e . key === "Enter" ) {
168+ handlePageInputSubmit ( ) ;
169+ }
170+ } }
171+ />
172+ < Button
173+ variant = "ghost"
174+ className = "absolute right-1 h-auto w-auto p-2 max-sm:hidden"
175+ onClick = { handlePageInputSubmit }
176+ >
177+ < ArrowUpRightIcon className = "size-4" />
178+ </ Button >
179+ </ div >
96180 </ PaginationContent >
97181 </ Pagination >
98182 ) ;
0 commit comments