mirror of
https://github.com/trafficlunar/tomodachi-share.git
synced 2026-06-28 06:34:15 +00:00
feat: image list in submit form
This commit is contained in:
parent
e83d7fadb5
commit
a1fcd46bda
5 changed files with 168 additions and 9 deletions
|
|
@ -2,8 +2,8 @@
|
|||
|
||||
import { redirect } from "next/navigation";
|
||||
|
||||
import { useEffect, useState } from "react";
|
||||
import { useDropzone } from "react-dropzone";
|
||||
import { useCallback, useEffect, useState } from "react";
|
||||
import { FileWithPath, useDropzone } from "react-dropzone";
|
||||
import { Icon } from "@iconify/react";
|
||||
|
||||
import { AES_CCM } from "@trafficlunar/asmcrypto.js";
|
||||
|
|
@ -16,11 +16,20 @@ import Mii from "@/utils/mii.js/mii";
|
|||
import TomodachiLifeMii from "@/utils/tomodachi-life-mii";
|
||||
|
||||
import TagSelector from "./submit/tag-selector";
|
||||
import ImageList from "./submit/image-list";
|
||||
import QrUpload from "./submit/qr-upload";
|
||||
import QrScanner from "./submit/qr-scanner";
|
||||
|
||||
export default function SubmitForm() {
|
||||
const { acceptedFiles, getRootProps, getInputProps } = useDropzone({
|
||||
const [files, setFiles] = useState<FileWithPath[]>([]);
|
||||
|
||||
const handleDrop = useCallback((acceptedFiles: FileWithPath[]) => {
|
||||
setFiles((prev) => [...prev, ...acceptedFiles]);
|
||||
}, []);
|
||||
|
||||
const { getRootProps, getInputProps } = useDropzone({
|
||||
onDrop: handleDrop,
|
||||
maxFiles: 3,
|
||||
accept: {
|
||||
"image/*": [".png", ".jpg", ".jpeg", ".bmp", ".webp"],
|
||||
},
|
||||
|
|
@ -154,14 +163,14 @@ export default function SubmitForm() {
|
|||
/>
|
||||
</div>
|
||||
|
||||
<div className="p-2 border-2 bg-orange-100 border-amber-500 rounded-2xl shadow-lg h-48">
|
||||
<div className="p-2 border-2 bg-orange-200 border-amber-500 rounded-2xl shadow-lg h-48">
|
||||
<div
|
||||
{...getRootProps({
|
||||
className:
|
||||
"bg-orange-100 flex flex-col justify-center items-center gap-2 p-4 rounded-xl border border-2 border-dashed border-amber-500 select-none h-full",
|
||||
"bg-orange-200 flex flex-col justify-center items-center gap-2 p-4 rounded-xl border border-2 border-dashed border-amber-500 select-none h-full",
|
||||
})}
|
||||
>
|
||||
<input {...getInputProps({ multiple: false })} />
|
||||
<input {...getInputProps()} />
|
||||
<Icon icon="material-symbols:upload" fontSize={64} />
|
||||
<p className="text-center">
|
||||
Drag and drop your images here
|
||||
|
|
@ -171,7 +180,7 @@ export default function SubmitForm() {
|
|||
</div>
|
||||
</div>
|
||||
|
||||
{/* todo: show file list here */}
|
||||
<ImageList files={files} setFiles={setFiles} />
|
||||
</div>
|
||||
|
||||
<div className="p-4 flex flex-col gap-2">
|
||||
|
|
|
|||
71
src/app/components/submit/image-list.tsx
Normal file
71
src/app/components/submit/image-list.tsx
Normal file
|
|
@ -0,0 +1,71 @@
|
|||
import { FileWithPath } from "react-dropzone";
|
||||
import { DragDropContext, Draggable, Droppable, DropResult } from "@hello-pangea/dnd";
|
||||
import { Icon } from "@iconify/react";
|
||||
|
||||
interface Props {
|
||||
files: readonly FileWithPath[];
|
||||
setFiles: React.Dispatch<React.SetStateAction<FileWithPath[]>>;
|
||||
}
|
||||
|
||||
export default function ImageList({ files, setFiles }: Props) {
|
||||
const handleDelete = (index: number) => {
|
||||
const newFiles = [...files];
|
||||
newFiles.splice(index, 1);
|
||||
setFiles(newFiles);
|
||||
};
|
||||
|
||||
const handleDragEnd = (result: DropResult) => {
|
||||
if (!result.destination) return;
|
||||
|
||||
const items = Array.from(files);
|
||||
const [reorderedItem] = items.splice(result.source.index, 1);
|
||||
items.splice(result.destination.index, 0, reorderedItem);
|
||||
|
||||
setFiles(items);
|
||||
};
|
||||
|
||||
return (
|
||||
<DragDropContext onDragEnd={handleDragEnd}>
|
||||
<Droppable droppableId="imageDroppable">
|
||||
{(provided) => (
|
||||
<div ref={provided.innerRef} {...provided.droppableProps} className="flex flex-col px-12">
|
||||
{files.map((file, index) => (
|
||||
<Draggable key={file.name} draggableId={file.name} index={index}>
|
||||
{(provided) => (
|
||||
<div
|
||||
ref={provided.innerRef}
|
||||
{...provided.draggableProps}
|
||||
className="w-full p-4 rounded-xl bg-orange-100 border-2 border-amber-500 flex gap-2 shadow-md my-1"
|
||||
>
|
||||
<img
|
||||
src={URL.createObjectURL(file)}
|
||||
alt={file.name}
|
||||
className="aspect-[3/2] object-contain w-24 rounded-md bg-orange-300 border-2 border-orange-400"
|
||||
/>
|
||||
<div className="flex flex-col justify-center w-full min-w-0">
|
||||
<span className="font-semibold text overflow-hidden text-ellipsis">{file.name}</span>
|
||||
<button
|
||||
onClick={() => handleDelete(index)}
|
||||
className="pill button text-xs w-min !px-3 !py-1 !bg-red-300 !border-red-400 hover:!bg-red-400"
|
||||
>
|
||||
Delete
|
||||
</button>
|
||||
</div>
|
||||
|
||||
<div
|
||||
{...provided.dragHandleProps}
|
||||
className="h-full w-11 px-1 cursor-grab flex items-center justify-center rounded transition-colors hover:bg-black/10"
|
||||
>
|
||||
<Icon icon="tabler:grip-horizontal" className="size-6 text-black/50" />
|
||||
</div>
|
||||
</div>
|
||||
)}
|
||||
</Draggable>
|
||||
))}
|
||||
{provided.placeholder}
|
||||
</div>
|
||||
)}
|
||||
</Droppable>
|
||||
</DragDropContext>
|
||||
);
|
||||
}
|
||||
|
|
@ -44,11 +44,11 @@ export default function QrUpload({ setQrBytesRaw }: Props) {
|
|||
});
|
||||
|
||||
return (
|
||||
<div className="p-2 border-2 bg-orange-100 border-amber-500 rounded-2xl shadow-lg w-full">
|
||||
<div className="p-2 border-2 bg-orange-200 border-amber-500 rounded-2xl shadow-lg w-full">
|
||||
<div
|
||||
{...getRootProps({
|
||||
className:
|
||||
"bg-orange-100 flex flex-col justify-center items-center gap-2 p-4 rounded-xl border border-2 border-dashed border-amber-500 select-none h-full",
|
||||
"bg-orange-200 flex flex-col justify-center items-center gap-2 p-4 rounded-xl border border-2 border-dashed border-amber-500 select-none h-full",
|
||||
})}
|
||||
>
|
||||
<input {...getInputProps({ multiple: false })} />
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue