Add reusable modal component

This commit is contained in:
wisplite
2025-11-20 17:36:33 -06:00
parent 33e925089e
commit 1fea638628
6 changed files with 114 additions and 1 deletions
+46
View File
@@ -0,0 +1,46 @@
import * as Dialog from '@radix-ui/react-dialog';
import { X } from 'lucide-react';
export default function Modal({ open, onOpenChange, trigger, title, children, isProtected = false }) {
return (
<Dialog.Root open={open} onOpenChange={onOpenChange}>
{trigger && <Dialog.Trigger asChild>{trigger}</Dialog.Trigger>}
<Dialog.Portal>
<Dialog.Overlay className="fixed inset-0 bg-black/60 z-50" />
<Dialog.Content
onInteractOutside={(e) => {
if (isProtected) e.preventDefault();
}}
onEscapeKeyDown={(e) => {
if (isProtected) {
if (!window.confirm("Are you sure you want to close this?")) {
e.preventDefault();
}
}
}}
className="fixed left-[50%] top-[50%] max-h-[85vh] w-[90vw] max-w-[450px] translate-x-[-50%] translate-y-[-50%] rounded-lg bg-[#141414] border border-[#2B2B2B] shadow-xl focus:outline-none z-50 overflow-hidden">
<div className="flex items-center justify-between px-4 py-3 border-b border-[#2B2B2B] bg-[#1A1A1A]">
<Dialog.Title className="text-sm font-bold text-white red-hat-text">
{title}
</Dialog.Title>
<Dialog.Close
onClick={(e) => {
if (isProtected) {
if (!window.confirm("Are you sure you want to close this?")) {
e.preventDefault();
}
}
}}
className="text-gray-400 hover:text-white transition-colors cursor-pointer outline-none">
<X className="w-4 h-4" />
</Dialog.Close>
</div>
<div className="p-4 text-gray-300 red-hat-text">
{children}
</div>
</Dialog.Content>
</Dialog.Portal>
</Dialog.Root>
);
}
@@ -0,0 +1,12 @@
import Modal from '../../components/Modal'
export default function AlbumCreateModal({ open, onOpenChange, trigger }) {
return (
<Modal open={open} onOpenChange={onOpenChange} trigger={trigger} title="Create Album">
<div className="flex flex-col gap-2">
<input type="text" placeholder="Name" className="w-full px-3 py-2.5 bg-[#141414] border border-[#2B2B2B] rounded-md text-white placeholder-gray-600 focus:outline-none focus:border-[#3B3B3B] transition-colors red-hat-text" />
<textarea type="text" placeholder="Description" className="w-full h-[20vh] px-3 py-2.5 bg-[#141414] border border-[#2B2B2B] rounded-md text-white placeholder-gray-600 focus:outline-none focus:border-[#3B3B3B] transition-colors red-hat-text resize-none" />
<button className="w-full py-2.5 bg-[#2B2B2B] hover:bg-[#3B3B3B] text-white rounded-md font-medium transition-colors red-hat-mono cursor-pointer mt-2">Create Album</button>
</div>
</Modal>
)
}
@@ -0,0 +1,15 @@
import { PlusIcon } from 'lucide-react'
import AlbumCreateModal from './AlbumCreateModal'
import { useState } from 'react'
export default function AlbumList() {
const [open, setOpen] = useState(false)
return (
<div className="flex flex-col items-center justify-start h-full w-full bg-[#141414]">
<div className="flex flex-row items-center justify-between gap-2 w-full px-6 py-4">
<h1 className="text-xl font-bold text-white red-hat-mono">Albums</h1>
<PlusIcon className="w-6 h-6 cursor-pointer" color="white" onClick={() => setOpen(true)} />
</div>
<AlbumCreateModal open={open} onOpenChange={setOpen} />
</div>
)
}
+2
View File
@@ -2,6 +2,7 @@ import NavBar from '../components/NavBar'
import { useLocation } from 'react-router-dom';
import { useAccount } from '../contexts/useAccount';
import { useEffect } from 'react';
import AlbumList from './components/AlbumList';
export default function Gallery() {
const currentPath = useLocation().pathname;
const pathList = currentPath.split('/').slice(1);
@@ -14,6 +15,7 @@ export default function Gallery() {
return (
<div className="flex flex-col items-center justify-start h-full w-full bg-[#141414]">
<NavBar path={pathList} />
<AlbumList />
</div>
)
}