From afe10604c838374c07a3bcc6cf7f7dd15d947047 Mon Sep 17 00:00:00 2001 From: Aditya Gupta Date: Sat, 6 Jun 2026 11:34:46 +0530 Subject: [PATCH] feat: add page transitions --- src/App.jsx | 29 ++++++++++++--------- src/components/BeanForm.jsx | 31 +++++++++++++++++++--- src/components/BrewForm.jsx | 43 ++++++++++++++++++++++++++----- src/components/CreateModal.jsx | 34 ++++++++++++++++++++---- src/components/IosPromptModal.jsx | 18 ++++++++++--- src/index.css | 15 +++++++++++ 6 files changed, 139 insertions(+), 31 deletions(-) diff --git a/src/App.jsx b/src/App.jsx index f393a86..03cc4bf 100644 --- a/src/App.jsx +++ b/src/App.jsx @@ -206,9 +206,10 @@ export default function CoffeeLogbook() { {/* Content */}
+ {/* ── Dashboard ── */} {/* ── Dashboard ── */} {view === "dashboard" && ( - <> +
{[{ num: beans.length, label: "Beans" }, { num: brewLogs.length, label: "Brews" }, { num: new Set(brewLogs.map(l => l.beanId)).size, label: "Tried" }].map(s => (
@@ -237,12 +238,12 @@ export default function CoffeeLogbook() { ) : brewLogs.sort((a, b) => b.createdAt - a.createdAt).slice(0, 5).map(log => ( ))} - +
)} {/* ── Bean Library ── */} {view === "beans" && !selectedBean && ( - <> +
Your Beans ({beans.length})
{beans.length === 0 ? (
@@ -273,21 +274,23 @@ export default function CoffeeLogbook() {
); })} - +
)} {/* ── Bean Detail ── */} {view === "beans" && selectedBean && ( - setSelectedBean(null)} - onEdit={() => { setEditingBean(selectedBean); setModal("editBean"); }} - onDelete={() => { if (confirm("Delete this bean and all its brew logs?")) deleteBean(selectedBean.id); }} - /> +
+ setSelectedBean(null)} + onEdit={() => { setEditingBean(selectedBean); setModal("editBean"); }} + onDelete={() => { if (confirm("Delete this bean and all its brew logs?")) deleteBean(selectedBean.id); }} + /> +
)} {/* ── Brew Logs ── */} {view === "brews" && ( - <> +
{METHODS.map(m => ( @@ -305,12 +308,14 @@ export default function CoffeeLogbook() { ) : filteredLogs.map(log => ( ))} - +
)} {/* ── Profile ── */} {view === "profile" && ( - +
+ +
)}
diff --git a/src/components/BeanForm.jsx b/src/components/BeanForm.jsx index 220f07d..efab8e8 100644 --- a/src/components/BeanForm.jsx +++ b/src/components/BeanForm.jsx @@ -1,11 +1,28 @@ -import React, { useState } from "react"; +import React, { useState, useEffect } from "react"; import { ROAST_TYPES, inputCls, labelCls } from "../constants"; export default function BeanForm({ onSave, onClose, initial }) { const [form, setForm] = useState(initial || { name: "", roastery: "", roastDate: "", roastType: "", image: "", tastingNotes: "" }); + const [active, setActive] = useState(false); const set = (k, v) => setForm(p => ({ ...p, [k]: v })); const canSave = form.name.trim().length > 0; + useEffect(() => { + const raf = requestAnimationFrame(() => setActive(true)); + return () => cancelAnimationFrame(raf); + }, []); + + const handleClose = (callback) => { + setActive(false); + setTimeout(() => { + if (typeof callback === "function") { + callback(); + } else { + onClose(); + } + }, 200); + }; + const handleImage = (e) => { const file = e.target.files?.[0]; if (!file) return; @@ -16,8 +33,14 @@ export default function BeanForm({ onSave, onClose, initial }) { }; return ( -
-
e.stopPropagation()}> +
handleClose()} + > +
e.stopPropagation()} + >
{initial ? "Edit Bean" : "Add Bean"}
@@ -50,7 +73,7 @@ export default function BeanForm({ onSave, onClose, initial }) {
Flavour profile from the bag or your own impressions
diff --git a/src/components/BrewForm.jsx b/src/components/BrewForm.jsx index 3606879..fcd0943 100644 --- a/src/components/BrewForm.jsx +++ b/src/components/BrewForm.jsx @@ -1,15 +1,38 @@ -import React, { useState } from "react"; +import React, { useState, useEffect } from "react"; import { METHODS, METHOD_LABELS, METHOD_ICONS, METHOD_COLORS, inputCls, labelCls } from "../constants"; export default function BrewForm({ beans, onSave, onClose }) { const [method, setMethod] = useState("pourover"); const [form, setForm] = useState({ beanId: beans[0]?.id || "" }); + const [active, setActive] = useState(false); const set = (k, v) => setForm(p => ({ ...p, [k]: v })); + useEffect(() => { + const raf = requestAnimationFrame(() => setActive(true)); + return () => cancelAnimationFrame(raf); + }, []); + + const handleClose = (callback) => { + setActive(false); + setTimeout(() => { + if (typeof callback === "function") { + callback(); + } else { + onClose(); + } + }, 200); + }; + if (beans.length === 0) { return ( -
-
e.stopPropagation()}> +
handleClose()} + > +
e.stopPropagation()} + >
Log a Brew
@@ -17,7 +40,7 @@ export default function BrewForm({ beans, onSave, onClose }) {

No beans yet

Add a bean to your library first.

- +
); @@ -47,8 +70,14 @@ export default function BrewForm({ beans, onSave, onClose }) { }; return ( -
-
e.stopPropagation()}> +
handleClose()} + > +
e.stopPropagation()} + >
Log a Brew
@@ -83,7 +112,7 @@ export default function BrewForm({ beans, onSave, onClose }) { set("tasteNotes", e.target.value)} />
+ onClick={() => handleClose(() => onSave({ ...form, method }))}>Save Brew Log
); diff --git a/src/components/CreateModal.jsx b/src/components/CreateModal.jsx index 337b610..b5fb71c 100644 --- a/src/components/CreateModal.jsx +++ b/src/components/CreateModal.jsx @@ -1,15 +1,39 @@ -import React from "react"; +import React, { useState, useEffect } from "react"; export default function CreateModal({ onClose, onAddBean, onAddBrew }) { + const [active, setActive] = useState(false); + + useEffect(() => { + // Trigger transition shortly after mounting + const raf = requestAnimationFrame(() => setActive(true)); + return () => cancelAnimationFrame(raf); + }, []); + + const handleClose = (callback) => { + setActive(false); + setTimeout(() => { + onClose(); + if (typeof callback === "function") { + callback(); + } + }, 200); + }; + return ( -
-
e.stopPropagation()}> +
handleClose()} + > +
e.stopPropagation()} + >
What would you like to add?