Started the New Proposal button dialog
This commit is contained in:
93
src/components/ImageInput.tsx
Normal file
93
src/components/ImageInput.tsx
Normal file
@@ -0,0 +1,93 @@
|
||||
import { typo } from "@styles/typography";
|
||||
import { cva } from "class-variance-authority";
|
||||
import type { ChangeEvent } from "react";
|
||||
import { useEffect, useRef, useState } from "react";
|
||||
import Image from "next/image";
|
||||
|
||||
const input = cva([
|
||||
typo({ tag: "p" }),
|
||||
"w-full rounded border-[6px] py-2 px-3 md:py-3 md:px-4 leading-tight border-fg placeholder-fg/50 bg-fg/20",
|
||||
"focus:outline-none",
|
||||
]);
|
||||
|
||||
interface ImageInputProps {
|
||||
label: string;
|
||||
placeholderImage?: string;
|
||||
onChange?: (file: File) => void;
|
||||
currentImage?: string;
|
||||
aspectRatio?: number;
|
||||
}
|
||||
|
||||
export const ImageInput: React.FC<ImageInputProps> = ({
|
||||
label,
|
||||
placeholderImage,
|
||||
onChange,
|
||||
currentImage,
|
||||
aspectRatio,
|
||||
}) => {
|
||||
const [preview, setPreview] = useState<string | undefined>(currentImage);
|
||||
const fileInputRef = useRef<HTMLInputElement | null>(null);
|
||||
|
||||
useEffect(() => {
|
||||
if (currentImage) {
|
||||
setPreview(currentImage);
|
||||
} else {
|
||||
setPreview(undefined);
|
||||
}
|
||||
}, [currentImage]);
|
||||
|
||||
const handleFileInputChange = (e: ChangeEvent<HTMLInputElement>) => {
|
||||
const file = e.target.files?.[0];
|
||||
if (file) {
|
||||
setPreview(URL.createObjectURL(file));
|
||||
if (onChange) onChange(file);
|
||||
}
|
||||
};
|
||||
|
||||
const handleImageClick = () => {
|
||||
fileInputRef.current?.click();
|
||||
};
|
||||
|
||||
return (
|
||||
<div className="flex flex-col">
|
||||
<label
|
||||
className={typo({ size: "base", className: "mb-1 block text-fg" })}
|
||||
>
|
||||
{label}
|
||||
</label>
|
||||
<div className="relative h-32 w-full overflow-clip rounded border-[6px] border-fg bg-fg/20">
|
||||
{preview ? (
|
||||
<div className={"h-full w-full"}>
|
||||
<img src={preview} alt="Selected image" className="object-cover" />
|
||||
</div>
|
||||
) : (
|
||||
<div
|
||||
className={"flex h-full w-full items-center justify-center"}
|
||||
onClick={handleImageClick}
|
||||
>
|
||||
{placeholderImage ? (
|
||||
<Image
|
||||
src={placeholderImage}
|
||||
alt="Placeholder image"
|
||||
className="object-cover"
|
||||
width={300}
|
||||
height={225}
|
||||
/>
|
||||
) : (
|
||||
<span className={typo({ tag: "p", className: "text-fg/50" })}>
|
||||
Click to add image
|
||||
</span>
|
||||
)}
|
||||
</div>
|
||||
)}
|
||||
<input
|
||||
type="file"
|
||||
accept="image/*"
|
||||
className="absolute inset-0 h-full w-full cursor-pointer opacity-0"
|
||||
onChange={handleFileInputChange}
|
||||
ref={fileInputRef}
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
};
|
||||
@@ -14,6 +14,11 @@ import {
|
||||
import type { IconType } from "react-icons/lib";
|
||||
import { Button } from "@components/Button";
|
||||
import * as DropdownMenu from "@radix-ui/react-dropdown-menu";
|
||||
import * as Dialog from "@radix-ui/react-dialog";
|
||||
import { cx } from "class-variance-authority";
|
||||
import { TextInput } from "@components/TextInput";
|
||||
import { Divider } from "@components/Divider";
|
||||
import { ImageInput } from "@components/ImageInput";
|
||||
|
||||
export const MainLayout: React.FC<Children> = ({ children }) => {
|
||||
return (
|
||||
@@ -72,7 +77,7 @@ export const SidePanel: React.FC = () => {
|
||||
</Link>
|
||||
))}
|
||||
</nav>
|
||||
<Button variant={{ size: "small" }}>New Proposal</Button>
|
||||
<NewProposalButton />
|
||||
</div>
|
||||
<MoreMenu />
|
||||
</header>
|
||||
@@ -80,6 +85,60 @@ export const SidePanel: React.FC = () => {
|
||||
);
|
||||
};
|
||||
|
||||
const NewProposalButton: React.FC = () => {
|
||||
const container =
|
||||
typeof window !== "undefined" ? document.getElementById("root") : null;
|
||||
|
||||
const FullDivider = () => (
|
||||
<div className="my-4 -mx-10">
|
||||
<Divider />
|
||||
</div>
|
||||
);
|
||||
|
||||
return (
|
||||
<Dialog.Root>
|
||||
<Dialog.Trigger asChild>
|
||||
<Button variant={{ size: "small" }}>New Proposal</Button>
|
||||
</Dialog.Trigger>
|
||||
|
||||
<Dialog.Portal container={container}>
|
||||
<Dialog.Overlay className="fixed inset-0 bg-black/40 data-[state=open]:animate-overlayShow" />
|
||||
|
||||
<Dialog.Content
|
||||
className={cx(
|
||||
"fixed top-[50%] left-[50%] max-h-[85vh] w-[90vw] max-w-[750px] translate-x-[-50%] translate-y-[-50%]",
|
||||
"rounded-3xl bg-bg p-10",
|
||||
"focus:outline-none data-[state=open]:animate-contentShow"
|
||||
)}
|
||||
>
|
||||
<Dialog.Title>
|
||||
<h1 className={typo({ tag: "h5", className: "text-primary" })}>
|
||||
Create new proposal
|
||||
</h1>
|
||||
</Dialog.Title>
|
||||
<Dialog.Description>
|
||||
Let others know a little about your project idea.
|
||||
</Dialog.Description>
|
||||
<FullDivider />
|
||||
<div className="mb-8 flex flex-col gap-y-4">
|
||||
<TextInput label="Title" />
|
||||
<TextInput
|
||||
label="Description"
|
||||
placeholder="Describe your project"
|
||||
/>
|
||||
<ImageInput label="Image" />
|
||||
</div>
|
||||
<Dialog.Close asChild>
|
||||
<div className="flex flex-row justify-end">
|
||||
<Button variant={{ size: "small" }}>Create</Button>
|
||||
</div>
|
||||
</Dialog.Close>
|
||||
</Dialog.Content>
|
||||
</Dialog.Portal>
|
||||
</Dialog.Root>
|
||||
);
|
||||
};
|
||||
|
||||
const MoreMenu: React.FC = () => {
|
||||
const container =
|
||||
typeof window !== "undefined" ? document.getElementById("root") : null;
|
||||
|
||||
Reference in New Issue
Block a user