1
- import { useState , useRef , useEffect } from "react" ;
2
- import { useIsMobile } from "@/hooks/use-mobile" ;
3
- import { Button } from "@/components/ui/button" ;
4
- import {
5
- ChevronRight ,
6
- ChevronLeft ,
7
- SkipBack ,
8
- SkipForward ,
9
- } from "lucide-react" ;
10
- import PageViewer from "./PageViewer" ;
1
+ import { useEffect , useRef , useState } from 'react' ;
2
+ import { useIsMobile } from '@/hooks/use-mobile' ;
3
+ import { Button } from '@/components/ui/button' ;
4
+ import { ChevronLeft , ChevronRight , SkipBack , SkipForward } from 'lucide-react' ;
5
+ import PageViewer from './PageViewer' ;
11
6
import { useLanguage } from '@/contexts/LanguageContext' ;
12
7
13
8
interface DocumentViewerProps {
14
9
className ?: string ;
15
10
}
16
11
17
12
export default function DocumentViewer ( { className } : DocumentViewerProps ) {
18
- const { t } = useLanguage ( ) ;
13
+ const { t , isRTL , currentLanguage } = useLanguage ( ) ;
19
14
const isMobile = useIsMobile ( ) ;
20
15
const totalPages = 604 ; // Total number of pages in the document
21
16
const [ currentPage , setCurrentPage ] = useState < number > ( 1 ) ;
22
17
23
- // ==== Improved "انتقال" Section state ====
24
- const [ gotoPage , setGotoPage ] = useState < string > ( "1" ) ;
18
+ // Navigation state
19
+ const [ gotoPage , setGotoPage ] = useState < string > ( '1' ) ;
25
20
const [ gotoError , setGotoError ] = useState < string | null > ( null ) ;
26
21
const inputRef = useRef < HTMLInputElement > ( null ) ;
27
22
28
- // ---- Pagination logic ----
23
+ // Calculate page increment based on view and RTL
29
24
const pageIncrement = isMobile ? 1 : 2 ;
30
25
26
+ // Adjust page number when switching between mobile/desktop
31
27
useEffect ( ( ) => {
32
28
if ( ! isMobile && currentPage % 2 === 0 ) {
33
29
setCurrentPage ( ( prev ) => prev - 1 ) ;
34
30
}
35
31
} , [ isMobile , currentPage ] ) ;
36
32
33
+ // Navigation handlers with RTL consideration
37
34
const goToNextPages = ( ) => {
38
35
setCurrentPage ( ( prev ) => Math . min ( totalPages , prev + pageIncrement ) ) ;
39
36
} ;
@@ -47,20 +44,14 @@ export default function DocumentViewer({ className }: DocumentViewerProps) {
47
44
} ;
48
45
49
46
const goToLastPage = ( ) => {
50
- setCurrentPage (
51
- isMobile
52
- ? totalPages
53
- : totalPages % 2 === 0
54
- ? totalPages - 1
55
- : totalPages
56
- ) ;
47
+ const lastPage = isMobile ? totalPages : totalPages % 2 === 0 ? totalPages - 1 : totalPages ;
48
+ setCurrentPage ( lastPage ) ;
57
49
} ;
58
50
59
- // ==== Improved Go To Page Logic ====
60
51
const handleGoto = ( ) => {
61
52
const page = parseInt ( gotoPage , 10 ) ;
62
53
if ( isNaN ( page ) || page < 1 || page > totalPages ) {
63
- setGotoError ( `رقم الصفحة يجب أن يكون بين 1 و ${ totalPages } ` ) ;
54
+ setGotoError ( t ( 'pageNumberError' , { min : 1 , max : totalPages } ) ) ;
64
55
inputRef . current ?. focus ( ) ;
65
56
} else {
66
57
setCurrentPage ( page ) ;
@@ -70,142 +61,160 @@ export default function DocumentViewer({ className }: DocumentViewerProps) {
70
61
}
71
62
} ;
72
63
73
- // Allow Enter key to trigger transition
74
64
const handleGotoInputKeyDown = ( e : React . KeyboardEvent < HTMLInputElement > ) => {
75
- if ( e . key === " Enter" ) {
65
+ if ( e . key === ' Enter' ) {
76
66
e . preventDefault ( ) ;
77
67
handleGoto ( ) ;
78
68
}
79
69
} ;
80
70
81
- // Keep goto field in sync if user changes page elsewhere
82
71
useEffect ( ( ) => {
83
72
setGotoPage ( String ( currentPage ) ) ;
84
73
setGotoError ( null ) ;
85
74
} , [ currentPage ] ) ;
86
75
76
+ const getPageDisplay = ( ) => {
77
+ if ( isMobile ) {
78
+ return [
79
+ < PageViewer
80
+ key = { currentPage }
81
+ pageNumber = { currentPage }
82
+ totalPages = { totalPages }
83
+ className = "animate-fadeIn aspect-[3/4] w-full max-w-md"
84
+ />
85
+ ] ;
86
+ }
87
+
88
+ return [
89
+ < PageViewer
90
+ key = { currentPage + 1 }
91
+ pageNumber = { currentPage + 1 }
92
+ totalPages = { totalPages }
93
+ className = "animate-slideInRight aspect-[3/4] w-1/2 max-w-md"
94
+ /> ,
95
+ < PageViewer
96
+ key = { currentPage }
97
+ pageNumber = { currentPage }
98
+ totalPages = { totalPages }
99
+ className = "animate-slideInLeft aspect-[3/4] w-1/2 max-w-md"
100
+ />
101
+ ] ;
102
+ } ;
103
+
104
+ const getNavigationIcons = ( ) => {
105
+ return isRTL ? {
106
+ first : < SkipForward className = "h-4 w-4" /> ,
107
+ previous : < ChevronRight className = "h-4 w-4" /> ,
108
+ next : < ChevronLeft className = "h-4 w-4" /> ,
109
+ last : < SkipBack className = "h-4 w-4" />
110
+ } : {
111
+ first : < SkipBack className = "h-4 w-4" /> ,
112
+ previous : < ChevronLeft className = "h-4 w-4" /> ,
113
+ next : < ChevronRight className = "h-4 w-4" /> ,
114
+ last : < SkipForward className = "h-4 w-4" />
115
+ } ;
116
+ } ;
117
+
118
+ const icons = getNavigationIcons ( ) ;
119
+
87
120
return (
88
- < div className = { className } >
121
+ < div className = { className } dir = { isRTL ? 'rtl' : 'ltr' } >
89
122
< div className = "flex flex-col items-center space-y-8" >
90
123
{ /* Pages container */ }
91
- < div className = "w-full flex flex-row-reverse items-center justify-center gap-1 md:gap-2 lg:gap-3" >
92
- { isMobile ? (
93
- // Mobile view - one page
94
- < PageViewer
95
- pageNumber = { currentPage }
96
- totalPages = { totalPages }
97
- className = "w-full aspect-[3/4] max-w-md animate-fadeIn"
98
- />
99
- ) : (
100
- // Desktop/tablet view - two pages
101
- < >
102
- { /* Left page (even) */ }
103
- < PageViewer
104
- pageNumber = { currentPage + 1 }
105
- totalPages = { totalPages }
106
- className = "w-1/2 aspect-[3/4] max-w-md animate-slideInLeft"
107
- />
108
-
109
- { /* Right page (odd) */ }
110
- < PageViewer
111
- pageNumber = { currentPage }
112
- totalPages = { totalPages }
113
- className = "w-1/2 aspect-[3/4] max-w-md animate-slideInRight"
114
- />
115
- </ >
116
- ) }
124
+ < div className = { `flex w-full items-center justify-center gap-1 md:gap-2 lg:gap-3 ${ isRTL ? 'flex-row-reverse' : 'flex-row' } ` } >
125
+ { getPageDisplay ( ) }
117
126
</ div >
118
127
119
128
{ /* Navigation controls */ }
120
- < div className = "flex items-center justify-center space-x-2 rtl:space-x-reverse " >
129
+ < div className = "flex items-center justify-center gap-2 " >
121
130
< Button
122
131
variant = "outline"
123
132
size = "icon"
124
133
onClick = { goToFirstPage }
125
134
disabled = { currentPage === 1 }
126
- title = "الصفحة الأولى"
135
+ title = { t ( 'firstPage' ) }
136
+ aria-label = { t ( 'firstPage' ) }
127
137
>
128
- < SkipForward className = "h-4 w-4" />
138
+ { icons . first }
129
139
</ Button >
130
140
131
141
< Button
132
142
variant = "outline"
133
143
size = "icon"
134
144
onClick = { goToPreviousPages }
135
145
disabled = { currentPage === 1 }
136
- title = "الصفحة السابقة"
146
+ title = { t ( 'previousPage' ) }
147
+ aria-label = { t ( 'previousPage' ) }
137
148
>
138
- < ChevronRight className = "h-4 w-4" />
149
+ { icons . previous }
139
150
</ Button >
140
151
141
152
< div className = "mx-4 text-sm font-medium" >
142
- < span > { currentPage } </ span >
153
+ < span > { currentPage . toLocaleString ( currentLanguage ) } </ span >
143
154
{ ! isMobile && currentPage + 1 <= totalPages && (
144
155
< >
145
156
< span className = "mx-1" > -</ span >
146
- < span > { currentPage + 1 } </ span >
157
+ < span > { ( currentPage + 1 ) . toLocaleString ( currentLanguage ) } </ span >
147
158
</ >
148
159
) }
149
- < span className = "mx-1 text-muted-foreground" > من </ span >
150
- < span > { totalPages } </ span >
160
+ < span className = "mx-1 text-muted-foreground" > { t ( 'of' ) } </ span >
161
+ < span > { totalPages . toLocaleString ( currentLanguage ) } </ span >
151
162
</ div >
152
163
153
164
< Button
154
165
variant = "outline"
155
166
size = "icon"
156
167
onClick = { goToNextPages }
157
- disabled = {
158
- isMobile
159
- ? currentPage === totalPages
160
- : currentPage + 1 >= totalPages
161
- }
162
- title = "الصفحة التالية"
168
+ disabled = { isMobile ? currentPage === totalPages : currentPage + 1 >= totalPages }
169
+ title = { t ( 'nextPage' ) }
170
+ aria-label = { t ( 'nextPage' ) }
163
171
>
164
- < ChevronLeft className = "h-4 w-4" />
172
+ { icons . next }
165
173
</ Button >
166
174
167
175
< Button
168
176
variant = "outline"
169
177
size = "icon"
170
178
onClick = { goToLastPage }
171
- disabled = {
172
- isMobile
173
- ? currentPage === totalPages
174
- : currentPage + 1 >= totalPages
175
- }
176
- title = "الصفحة الأخيرة"
179
+ disabled = { isMobile ? currentPage === totalPages : currentPage + 1 >= totalPages }
180
+ title = { t ( 'lastPage' ) }
181
+ aria-label = { t ( 'lastPage' ) }
177
182
>
178
- < SkipBack className = "h-4 w-4" />
183
+ { icons . last }
179
184
</ Button >
180
185
</ div >
181
186
182
- { /* Improved Page number input */ }
187
+ { /* Page number input */ }
183
188
< div className = "flex flex-col items-center gap-1" >
184
- < label htmlFor = "goto-input" className = "text-right w-full text-xs md:text-sm font-bold mb-1 text-muted-foreground" >
189
+ < label
190
+ htmlFor = "goto-input"
191
+ className = "mb-1 w-full text-xs font-bold text-muted-foreground md:text-sm"
192
+ style = { { textAlign : isRTL ? 'right' : 'left' } }
193
+ >
185
194
{ t ( 'goToPage' ) }
186
195
</ label >
187
- < div className = "flex items-center space-x-2 rtl:space-x-reverse " >
196
+ < div className = "flex items-center gap-2 " >
188
197
< input
189
198
id = "goto-input"
190
199
ref = { inputRef }
191
200
type = "number"
192
201
min = { 1 }
193
202
max = { totalPages }
194
203
value = { gotoPage }
195
- onChange = { ( e ) => setGotoPage ( e . target . value . replace ( / \D / , "" ) ) }
204
+ onChange = { ( e ) => setGotoPage ( e . target . value . replace ( / \D / , '' ) ) }
196
205
onKeyDown = { handleGotoInputKeyDown }
197
- className = { `w-20 md:w-28 h-10 px-2 border rounded text-center focus:ring-2 focus:ring-primary transition-all` }
198
- aria-label = "رقم الصفحة"
206
+ className = { `h-10 w-20 rounded border px-2 text-center transition-all focus:ring-2 focus:ring-primary md:w-28 ${
207
+ isRTL ? 'text-right' : 'text-left'
208
+ } `}
209
+ aria-label = { t ( 'pageNumber' ) }
210
+ dir = "ltr" // Always LTR for numbers
199
211
/>
200
- < Button
201
- variant = "secondary"
202
- onClick = { handleGoto }
203
- >
212
+ < Button variant = "secondary" onClick = { handleGoto } >
204
213
{ t ( 'goTo' ) }
205
214
</ Button >
206
215
</ div >
207
216
{ gotoError && (
208
- < div className = "mt-2 text-xs text-red-500 animate-fadeIn " role = "alert" >
217
+ < div className = "animate-fadeIn mt-2 text-xs text-red-500" role = "alert" >
209
218
{ gotoError }
210
219
</ div >
211
220
) }
0 commit comments