From 77349f2b90df831440176dab39f8afe050f7bc7c Mon Sep 17 00:00:00 2001 From: Aditya Gupta Date: Sat, 6 Jun 2026 22:36:19 +0530 Subject: [PATCH] refactor: Made modal a reusable component --- src/components/BeanForm.jsx | 155 ++++++---------------- src/components/BrewForm.jsx | 209 ++++++++---------------------- src/components/CreateModal.jsx | 131 ++++--------------- src/components/IosPromptModal.jsx | 157 +++++++--------------- src/components/Modal.jsx | 101 +++++++++++++++ 5 files changed, 272 insertions(+), 481 deletions(-) create mode 100644 src/components/Modal.jsx diff --git a/src/components/BeanForm.jsx b/src/components/BeanForm.jsx index c096b74..58bb864 100644 --- a/src/components/BeanForm.jsx +++ b/src/components/BeanForm.jsx @@ -1,59 +1,12 @@ -import { useState, useEffect } from "react"; +import { useState } from "react"; import { ROAST_TYPES, inputCls, labelCls } from "../constants"; +import Modal from "./Modal"; export default function BeanForm({ onSave, onClose, initial }) { const [form, setForm] = useState(initial || { name: "", roastery: "", roastDate: "", roastType: "", image: "", tastingNotes: "" }); - const [active, setActive] = useState(false); - const [dragY, setDragY] = useState(0); - const [isDragging, setIsDragging] = useState(false); - const [startY, setStartY] = useState(0); const set = (k, v) => setForm(p => ({ ...p, [k]: v })); const canSave = form.name.trim().length > 0; - useEffect(() => { - // Prevent background scroll when mounted - document.body.style.overflow = "hidden"; - const raf = requestAnimationFrame(() => setActive(true)); - return () => { - cancelAnimationFrame(raf); - document.body.style.overflow = ""; - }; - }, []); - - const handleClose = (callback) => { - setActive(false); - setTimeout(() => { - if (typeof callback === "function") { - callback(); - } else { - onClose(); - } - }, 200); - }; - - const handleDragStart = (clientY) => { - setIsDragging(true); - setStartY(clientY); - }; - - const handleDragMove = (clientY) => { - if (!isDragging) return; - const deltaY = clientY - startY; - if (deltaY > 0) { - setDragY(deltaY); - } - }; - - const handleDragEnd = () => { - if (!isDragging) return; - setIsDragging(false); - if (dragY > 100) { - handleClose(); - } else { - setDragY(0); - } - }; - const handleImage = (e) => { const file = e.target.files?.[0]; if (!file) return; @@ -64,71 +17,45 @@ export default function BeanForm({ onSave, onClose, initial }) { }; return ( -
handleClose()} - > -
e.stopPropagation()} - style={{ - transform: isDragging - ? `translateY(${dragY}px)` - : active - ? `translateY(${dragY}px)` - : "translateY(100%)", - transition: isDragging ? "none" : "transform 0.2s ease-out" - }} - > -
handleDragStart(e.touches[0].clientY)} - onTouchMove={(e) => handleDragMove(e.touches[0].clientY)} - onTouchEnd={handleDragEnd} - onMouseDown={(e) => handleDragStart(e.clientY)} - onMouseMove={(e) => { - if (e.buttons === 1) handleDragMove(e.clientY); - }} - onMouseUp={handleDragEnd} - onMouseLeave={handleDragEnd} - > -
-
-
{initial ? "Edit Bean" : "Add Bean"}
-
- set("name", e.target.value)} />
-
- set("roastery", e.target.value)} />
-
-
- set("roastDate", e.target.value)} />
-
-
-
-
- -
- {form.image ? ( - <>Bean - - ) : (<>
📷
Tap to upload a photo
)} - + + {(close) => ( + <> +
{initial ? "Edit Bean" : "Add Bean"}
+
+ set("name", e.target.value)} />
+
+ set("roastery", e.target.value)} />
+
+
+ set("roastDate", e.target.value)} />
+
+
-
Max 2 MB · JPG, PNG, or WebP
-
-
-