@@ -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" >
38- < PaginationLink
39- onClick = { ( ) => {
40- setPage ( activePage - 2 ) ;
41- } }
42- >
43- { activePage - 2 }
44- </ PaginationLink >
45- </ PaginationItem >
80+ < >
81+ < PaginationItem >
82+ < PaginationLink
83+ onClick = { ( ) => {
84+ setPage ( 1 ) ;
85+ } }
86+ >
87+ 1
88+ </ PaginationLink >
89+ </ PaginationItem >
90+
91+ < PaginationItem >
92+ < PaginationEllipsis className = "max-sm:w-3" />
93+ </ PaginationItem >
94+ </ >
4695 ) }
96+
4797 { activePage - 1 > 0 && (
48- < PaginationItem >
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" >
74- < PaginationLink
75- onClick = { ( ) => {
76- setPage ( activePage + 2 ) ;
77- } }
78- >
79- { activePage + 2 }
80- </ PaginationLink >
81- </ PaginationItem >
82- ) }
124+
125+ { /* ... + Last page */ }
83126 { activePage + 3 <= totalPages && (
84- < PaginationItem className = "max-sm:hidden" >
85- < PaginationEllipsis />
86- </ PaginationItem >
127+ < >
128+ < PaginationItem >
129+ < PaginationEllipsis className = "max-sm:w-3" />
130+ </ PaginationItem >
131+
132+ < PaginationItem >
133+ < PaginationLink
134+ onClick = { ( ) => {
135+ setPage ( totalPages ) ;
136+ } }
137+ >
138+ { totalPages }
139+ </ PaginationLink >
140+ </ PaginationItem >
141+ </ >
87142 ) }
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