style: redesign submit form
This commit is contained in:
parent
eefb1a0c37
commit
ff13f85dee
6 changed files with 87 additions and 60 deletions
|
|
@ -21,7 +21,7 @@ export default function Carousel({ images, className }: Props) {
|
||||||
if (!emblaApi) return;
|
if (!emblaApi) return;
|
||||||
setScrollSnaps(emblaApi.scrollSnapList());
|
setScrollSnaps(emblaApi.scrollSnapList());
|
||||||
emblaApi.on("select", () => setSelectedIndex(emblaApi.selectedScrollSnap()));
|
emblaApi.on("select", () => setSelectedIndex(emblaApi.selectedScrollSnap()));
|
||||||
}, [emblaApi]);
|
}, [images, emblaApi]);
|
||||||
|
|
||||||
// Handle keyboard events
|
// Handle keyboard events
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
|
|
|
||||||
|
|
@ -159,7 +159,7 @@ export default function ImageViewer({ src, alt, width, height, className, images
|
||||||
|
|
||||||
{/* Carousel snaps */}
|
{/* Carousel snaps */}
|
||||||
<div
|
<div
|
||||||
className={`z-50 flex justify-center gap-2 absolute left-1/2 -translate-x-1/2 bottom-4 transition-opacity duration-300 ${
|
className={`z-50 flex justify-center gap-3 absolute left-1/2 -translate-x-1/2 bottom-4 transition-opacity duration-300 ${
|
||||||
isVisible ? "opacity-100" : "opacity-0"
|
isVisible ? "opacity-100" : "opacity-0"
|
||||||
}`}
|
}`}
|
||||||
>
|
>
|
||||||
|
|
@ -167,7 +167,7 @@ export default function ImageViewer({ src, alt, width, height, className, images
|
||||||
<button
|
<button
|
||||||
key={index}
|
key={index}
|
||||||
onClick={() => emblaApi?.scrollTo(index)}
|
onClick={() => emblaApi?.scrollTo(index)}
|
||||||
className={`size-3 cursor-pointer rounded-full ${index === selectedIndex ? "bg-black" : "bg-black/25"}`}
|
className={`size-2.5 cursor-pointer rounded-full ${index === selectedIndex ? "bg-black" : "bg-black/25"}`}
|
||||||
/>
|
/>
|
||||||
))}
|
))}
|
||||||
</div>
|
</div>
|
||||||
|
|
|
||||||
|
|
@ -41,6 +41,8 @@ export default function ImageList({ files, setFiles }: Props) {
|
||||||
<Image
|
<Image
|
||||||
src={URL.createObjectURL(file)}
|
src={URL.createObjectURL(file)}
|
||||||
alt={file.name}
|
alt={file.name}
|
||||||
|
width={96}
|
||||||
|
height={96}
|
||||||
className="aspect-[3/2] object-contain w-24 rounded-md bg-orange-300 border-2 border-orange-400"
|
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">
|
<div className="flex flex-col justify-center w-full min-w-0">
|
||||||
|
|
|
||||||
|
|
@ -1,7 +1,6 @@
|
||||||
"use client";
|
"use client";
|
||||||
|
|
||||||
import { redirect } from "next/navigation";
|
import { redirect } from "next/navigation";
|
||||||
import Image from "next/image";
|
|
||||||
|
|
||||||
import { useCallback, useEffect, useState } from "react";
|
import { useCallback, useEffect, useState } from "react";
|
||||||
import { FileWithPath, useDropzone } from "react-dropzone";
|
import { FileWithPath, useDropzone } from "react-dropzone";
|
||||||
|
|
@ -18,6 +17,8 @@ import TagSelector from "../tag-selector";
|
||||||
import ImageList from "./image-list";
|
import ImageList from "./image-list";
|
||||||
import QrUpload from "./qr-upload";
|
import QrUpload from "./qr-upload";
|
||||||
import QrScanner from "./qr-scanner";
|
import QrScanner from "./qr-scanner";
|
||||||
|
import LikeButton from "../like-button";
|
||||||
|
import Carousel from "../carousel";
|
||||||
|
|
||||||
export default function SubmitForm() {
|
export default function SubmitForm() {
|
||||||
const [files, setFiles] = useState<FileWithPath[]>([]);
|
const [files, setFiles] = useState<FileWithPath[]>([]);
|
||||||
|
|
@ -106,7 +107,7 @@ export default function SubmitForm() {
|
||||||
}
|
}
|
||||||
|
|
||||||
try {
|
try {
|
||||||
setStudioUrl(conversion.mii.studioUrl({ width: 128 }));
|
setStudioUrl(conversion.mii.studioUrl({ width: 512 }));
|
||||||
|
|
||||||
// Generate a new QR code for aesthetic reasons
|
// Generate a new QR code for aesthetic reasons
|
||||||
const byteString = String.fromCharCode(...qrBytes);
|
const byteString = String.fromCharCode(...qrBytes);
|
||||||
|
|
@ -124,53 +125,46 @@ export default function SubmitForm() {
|
||||||
}, [qrBytesRaw]);
|
}, [qrBytesRaw]);
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<form onSubmit={handleSubmit} className="grid grid-cols-2 max-md:grid-cols-1">
|
<form onSubmit={handleSubmit} className="flex justify-center gap-4 w-full max-lg:flex-col max-lg:items-center">
|
||||||
<div className="p-4 flex flex-col gap-2 max-md:order-2">
|
<div className="flex justify-center">
|
||||||
<div className="flex justify-center gap-2">
|
<div className="w-[18.75rem] h-min flex flex-col bg-zinc-50 rounded-3xl border-2 border-zinc-300 shadow-lg p-3">
|
||||||
<div className="relative flex justify-center items-center size-33 aspect-square bg-orange-100 rounded-xl border-2 border-amber-500">
|
<Carousel
|
||||||
{!studioUrl && <span className="absolute text-center font-light">Mii</span>}
|
images={[studioUrl ?? "/missing.webp", generatedQrCodeUrl ?? "/missing.webp", ...files.map((file) => URL.createObjectURL(file))]}
|
||||||
<Image
|
|
||||||
src={studioUrl ?? "/missing.webp"}
|
|
||||||
width={128}
|
|
||||||
height={128}
|
|
||||||
quality={100}
|
|
||||||
alt="Nintendo Studio URL"
|
|
||||||
className="size-full rounded-xl text-[0px]"
|
|
||||||
/>
|
/>
|
||||||
|
|
||||||
|
<div className="p-4 flex flex-col gap-1 h-full">
|
||||||
|
<h1 className="font-bold text-2xl line-clamp-1" title={name}>
|
||||||
|
{name || "Mii name"}
|
||||||
|
</h1>
|
||||||
|
<div id="tags" className="flex flex-wrap gap-1">
|
||||||
|
{tags.length == 0 && <span className="px-2 py-1 bg-orange-300 rounded-full text-xs">tag</span>}
|
||||||
|
{tags.map((tag) => (
|
||||||
|
<span key={tag} className="px-2 py-1 bg-orange-300 rounded-full text-xs">
|
||||||
|
{tag}
|
||||||
|
</span>
|
||||||
|
))}
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div className="mt-auto">
|
||||||
|
<LikeButton likes={0} isLiked={false} disabled />
|
||||||
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<div className="relative flex justify-center items-center size-33 aspect-square bg-orange-100 rounded-xl border-2 border-amber-500">
|
|
||||||
{!generatedQrCodeUrl && <span className="absolute text-center font-light">QR Code</span>}
|
|
||||||
<Image
|
|
||||||
src={generatedQrCodeUrl ?? "/missing.webp"}
|
|
||||||
width={128}
|
|
||||||
height={128}
|
|
||||||
alt="Generated QR Code"
|
|
||||||
className="size-full rounded-xl text-[0px]"
|
|
||||||
/>
|
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div className="p-2 border-2 bg-orange-200 border-amber-500 rounded-2xl shadow-lg h-48">
|
<div className="bg-amber-50 border-2 border-amber-500 rounded-2xl shadow-lg p-4 flex flex-col gap-2 max-w-2xl w-full">
|
||||||
<div
|
<div>
|
||||||
{...getRootProps({
|
<h2 className="text-2xl font-bold">Submit your Mii</h2>
|
||||||
className:
|
<p className="text-sm text-zinc-500">Share your creation for others to see.</p>
|
||||||
"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()} />
|
|
||||||
<Icon icon="material-symbols:upload" fontSize={64} />
|
|
||||||
<p className="text-center">
|
|
||||||
Drag and drop your images here
|
|
||||||
<br />
|
|
||||||
or click to open
|
|
||||||
</p>
|
|
||||||
</div>
|
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<ImageList files={files} setFiles={setFiles} />
|
{/* Separator */}
|
||||||
|
<div className="flex items-center gap-4 text-zinc-500 text-sm font-medium my-1">
|
||||||
|
<hr className="flex-grow border-zinc-300" />
|
||||||
|
<span>Info</span>
|
||||||
|
<hr className="flex-grow border-zinc-300" />
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div className="p-4 flex flex-col gap-2">
|
|
||||||
<div className="w-full grid grid-cols-3 items-center">
|
<div className="w-full grid grid-cols-3 items-center">
|
||||||
<label htmlFor="name" className="font-semibold">
|
<label htmlFor="name" className="font-semibold">
|
||||||
Name
|
Name
|
||||||
|
|
@ -194,9 +188,14 @@ export default function SubmitForm() {
|
||||||
<TagSelector tags={tags} setTags={setTags} />
|
<TagSelector tags={tags} setTags={setTags} />
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<fieldset className="border-t-2 border-b-2 border-black p-3 flex flex-col items-center gap-2">
|
{/* Separator */}
|
||||||
<legend className="px-2">QR Code</legend>
|
<div className="flex items-center gap-4 text-zinc-500 text-sm font-medium mt-8 mb-2">
|
||||||
|
<hr className="flex-grow border-zinc-300" />
|
||||||
|
<span>QR Code</span>
|
||||||
|
<hr className="flex-grow border-zinc-300" />
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div className="flex flex-col items-center gap-2">
|
||||||
<QrUpload setQrBytesRaw={setQrBytesRaw} />
|
<QrUpload setQrBytesRaw={setQrBytesRaw} />
|
||||||
|
|
||||||
<span>or</span>
|
<span>or</span>
|
||||||
|
|
@ -207,15 +206,42 @@ export default function SubmitForm() {
|
||||||
</button>
|
</button>
|
||||||
|
|
||||||
<QrScanner isOpen={isQrScannerOpen} setIsOpen={setIsQrScannerOpen} setQrBytesRaw={setQrBytesRaw} />
|
<QrScanner isOpen={isQrScannerOpen} setIsOpen={setIsQrScannerOpen} setQrBytesRaw={setQrBytesRaw} />
|
||||||
</fieldset>
|
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div className="flex flex-col justify-center items-center gap-2 px-4 min-md:col-span-2 max-md:order-3">
|
{/* Separator */}
|
||||||
<button type="submit" className="pill button w-min">
|
<div className="flex items-center gap-4 text-zinc-500 text-sm font-medium mt-8 mb-2">
|
||||||
|
<hr className="flex-grow border-zinc-300" />
|
||||||
|
<span>Custom images</span>
|
||||||
|
<hr className="flex-grow border-zinc-300" />
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div className="max-w-md w-full self-center">
|
||||||
|
<div
|
||||||
|
{...getRootProps({
|
||||||
|
className:
|
||||||
|
"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()} />
|
||||||
|
<Icon icon="material-symbols:upload" fontSize={48} />
|
||||||
|
<p className="text-center text-sm">
|
||||||
|
Drag and drop your images here
|
||||||
|
<br />
|
||||||
|
or click to open
|
||||||
|
</p>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<ImageList files={files} setFiles={setFiles} />
|
||||||
|
|
||||||
|
<hr className="border-zinc-300 my-2" />
|
||||||
|
<div className="flex justify-between items-center">
|
||||||
|
{error && <span className="text-red-400 font-bold">Error: {error}</span>}
|
||||||
|
|
||||||
|
<button type="submit" className="pill button w-min ml-auto">
|
||||||
Submit
|
Submit
|
||||||
</button>
|
</button>
|
||||||
|
</div>
|
||||||
{error && <span className="text-red-400 font-bold">Error: {error}</span>}
|
|
||||||
</div>
|
</div>
|
||||||
</form>
|
</form>
|
||||||
);
|
);
|
||||||
|
|
|
||||||
|
|
@ -52,7 +52,7 @@ export default function QrUpload({ setQrBytesRaw }: Props) {
|
||||||
});
|
});
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<div className="p-2 border-2 bg-orange-200 border-amber-500 rounded-2xl shadow-lg w-full">
|
<div className="max-w-md w-full">
|
||||||
<div
|
<div
|
||||||
{...getRootProps({
|
{...getRootProps({
|
||||||
className:
|
className:
|
||||||
|
|
|
||||||
|
|
@ -8,8 +8,7 @@ export default async function SubmitPage() {
|
||||||
if (!session) redirect("/login");
|
if (!session) redirect("/login");
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<div>
|
<div className="flex justify-center">
|
||||||
<h1 className="text-2xl font-bold text-center">Submit your Mii</h1>
|
|
||||||
<SubmitForm />
|
<SubmitForm />
|
||||||
</div>
|
</div>
|
||||||
);
|
);
|
||||||
|
|
|
||||||
Loading…
Reference in a new issue