1
1
import { Button } from '@/components/ui/button'
2
+ import {
3
+ Carousel ,
4
+ CarouselApi ,
5
+ CarouselContent ,
6
+ CarouselItem ,
7
+ CarouselNext ,
8
+ CarouselPrevious ,
9
+ } from '@/components/ui/carousel'
2
10
import {
3
11
Dialog ,
4
12
DialogClose ,
@@ -12,7 +20,7 @@ import { ExpenseFormValues } from '@/lib/schemas'
12
20
import { Loader2 , Plus , Trash , X } from 'lucide-react'
13
21
import { getImageData , useS3Upload } from 'next-s3-upload'
14
22
import Image from 'next/image'
15
- import { useState } from 'react'
23
+ import { useEffect , useState } from 'react'
16
24
17
25
type Props = {
18
26
documents : ExpenseFormValues [ 'documents' ]
@@ -61,8 +69,9 @@ export function ExpenseDocumentsInput({ documents, updateDocuments }: Props) {
61
69
< DocumentThumbnail
62
70
key = { doc . id }
63
71
document = { doc }
64
- deleteDocument = { ( ) => {
65
- updateDocuments ( documents . filter ( ( d ) => d . id !== doc . id ) )
72
+ documents = { documents }
73
+ deleteDocument = { ( document ) => {
74
+ updateDocuments ( documents . filter ( ( d ) => d . id !== document . id ) )
66
75
} }
67
76
/>
68
77
) ) }
@@ -89,12 +98,27 @@ export function ExpenseDocumentsInput({ documents, updateDocuments }: Props) {
89
98
90
99
export function DocumentThumbnail ( {
91
100
document,
101
+ documents,
92
102
deleteDocument,
93
103
} : {
94
104
document : ExpenseFormValues [ 'documents' ] [ number ]
95
- deleteDocument : ( ) => void
105
+ documents : ExpenseFormValues [ 'documents' ]
106
+ deleteDocument : ( document : ExpenseFormValues [ 'documents' ] [ number ] ) => void
96
107
} ) {
97
108
const [ open , setOpen ] = useState ( false )
109
+ const [ api , setApi ] = useState < CarouselApi > ( )
110
+ const [ currentDocument , setCurrentDocument ] = useState < number | null > ( null )
111
+
112
+ useEffect ( ( ) => {
113
+ if ( ! api ) return
114
+
115
+ api . on ( 'slidesInView' , ( ) => {
116
+ const index = api . slidesInView ( ) [ 0 ]
117
+ if ( index !== undefined ) {
118
+ setCurrentDocument ( index )
119
+ }
120
+ } )
121
+ } , [ api ] )
98
122
99
123
return (
100
124
< Dialog open = { open } onOpenChange = { setOpen } >
@@ -112,33 +136,54 @@ export function DocumentThumbnail({
112
136
/>
113
137
</ Button >
114
138
</ DialogTrigger >
115
- < DialogContent className = "p-4 w-fit min-w-[300px] min-h-[300px] max-w-full [&>:last-child]:hidden" >
116
- < div className = "flex justify-end" >
117
- < Button
118
- variant = "ghost"
119
- className = "text-destructive"
120
- onClick = { ( ) => {
121
- deleteDocument ( )
122
- setOpen ( false )
139
+ < DialogContent className = "p-4 w-[100vw] max-w-[100vw] h-[100dvh] max-h-[100dvh] sm:max-w-[calc(100vw-32px)] sm:max-h-[calc(100dvh-32px)] [&>:last-child]:hidden" >
140
+ < div className = "flex flex-col gap-4" >
141
+ < div className = "flex justify-end" >
142
+ < Button
143
+ variant = "ghost"
144
+ className = "text-destructive"
145
+ onClick = { ( ) => {
146
+ if ( currentDocument !== null ) {
147
+ deleteDocument ( documents [ currentDocument ] )
148
+ }
149
+ setOpen ( false )
150
+ } }
151
+ >
152
+ < Trash className = "w-4 h-4 mr-2" />
153
+ Delete document
154
+ </ Button >
155
+ < DialogClose asChild >
156
+ < Button variant = "ghost" >
157
+ < X className = "w-4 h-4 mr-2" /> Close
158
+ </ Button >
159
+ </ DialogClose >
160
+ </ div >
161
+
162
+ < Carousel
163
+ opts = { {
164
+ startIndex : documents . indexOf ( document ) ,
165
+ loop : true ,
166
+ align : 'center' ,
123
167
} }
168
+ setApi = { setApi }
124
169
>
125
- < Trash className = "w-4 h-4 mr-2" />
126
- Delete document
127
- </ Button >
128
- < DialogClose asChild >
129
- < Button variant = "ghost" >
130
- < X className = "w-4 h-4 mr-2" /> Close
131
- </ Button >
132
- </ DialogClose >
170
+ < CarouselContent >
171
+ { documents . map ( ( document , index ) => (
172
+ < CarouselItem key = { index } >
173
+ < Image
174
+ className = "object-contain w-[calc(100vw-32px)] h-[calc(100dvh-32px-40px-16px)] sm:w-[calc(100vw-32px-32px)] sm:h-[calc(100dvh-32px-40px-16px-32px)]"
175
+ src = { document . url }
176
+ width = { document . width }
177
+ height = { document . height }
178
+ alt = ""
179
+ />
180
+ </ CarouselItem >
181
+ ) ) }
182
+ </ CarouselContent >
183
+ < CarouselPrevious className = "left-0 top-auto bottom-0" />
184
+ < CarouselNext className = "right-0 top-auto bottom-0" />
185
+ </ Carousel >
133
186
</ div >
134
- { /* eslint-disable-next-line @next/next/no-img-element */ }
135
- < Image
136
- className = "object-contain w-[100vw] h-[100dvh] max-w-[calc(100vw-32px)] max-h-[calc(100dvh-32px-40px-16px)] sm:w-fit sm:h-fit sm:max-w-[calc(100vw-32px-32px)] sm:max-h-[calc(100dvh-32px-40px-32px)]"
137
- src = { document . url }
138
- width = { document . width }
139
- height = { document . height }
140
- alt = ""
141
- />
142
187
</ DialogContent >
143
188
</ Dialog >
144
189
)
0 commit comments