Skip to content

Commit 7c3a429

Browse files
committed
Merge remote-tracking branch 'base/main'
2 parents 3cf5e2e + 9876d70 commit 7c3a429

File tree

4 files changed

+362
-28
lines changed

4 files changed

+362
-28
lines changed

package-lock.json

+26
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

package.json

+1
Original file line numberDiff line numberDiff line change
@@ -39,6 +39,7 @@
3939
"cmdk": "^0.2.0",
4040
"content-disposition": "^0.5.4",
4141
"dayjs": "^1.11.10",
42+
"embla-carousel-react": "^8.0.0-rc21",
4243
"lucide-react": "^0.290.0",
4344
"nanoid": "^5.0.4",
4445
"next": "^14.1.0",

src/components/expense-documents-input.tsx

+73-28
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,12 @@
11
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'
210
import {
311
Dialog,
412
DialogClose,
@@ -12,7 +20,7 @@ import { ExpenseFormValues } from '@/lib/schemas'
1220
import { Loader2, Plus, Trash, X } from 'lucide-react'
1321
import { getImageData, useS3Upload } from 'next-s3-upload'
1422
import Image from 'next/image'
15-
import { useState } from 'react'
23+
import { useEffect, useState } from 'react'
1624

1725
type Props = {
1826
documents: ExpenseFormValues['documents']
@@ -61,8 +69,9 @@ export function ExpenseDocumentsInput({ documents, updateDocuments }: Props) {
6169
<DocumentThumbnail
6270
key={doc.id}
6371
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))
6675
}}
6776
/>
6877
))}
@@ -89,12 +98,27 @@ export function ExpenseDocumentsInput({ documents, updateDocuments }: Props) {
8998

9099
export function DocumentThumbnail({
91100
document,
101+
documents,
92102
deleteDocument,
93103
}: {
94104
document: ExpenseFormValues['documents'][number]
95-
deleteDocument: () => void
105+
documents: ExpenseFormValues['documents']
106+
deleteDocument: (document: ExpenseFormValues['documents'][number]) => void
96107
}) {
97108
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])
98122

99123
return (
100124
<Dialog open={open} onOpenChange={setOpen}>
@@ -112,33 +136,54 @@ export function DocumentThumbnail({
112136
/>
113137
</Button>
114138
</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',
123167
}}
168+
setApi={setApi}
124169
>
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>
133186
</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-
/>
142187
</DialogContent>
143188
</Dialog>
144189
)

0 commit comments

Comments
 (0)