navbar skeleton

This commit is contained in:
Ruidy Nemausat 2020-02-04 15:10:44 +01:00
parent 99e61fdd74
commit 0c1e426669
10 changed files with 263 additions and 181 deletions

View file

@ -97,7 +97,8 @@ Free meal planner for cooks short on ideas! (like me …)
1. add sidenav on mobile 1. add sidenav on mobile
1. send message after contact form validation (confirm to sender and msg+info to admin) 1. send message after contact form validation (confirm to sender and msg+info to admin)
1. Breadcrumb 1. Design & Breadcrumb
1. Cookie bar
1. code cleanup (props and refactoring) 1. code cleanup (props and refactoring)
1. Back to top button 1. Back to top button
1. Take a look at some components [here](http://react-materialize.github.io/react-materialize/?path=/story/css-grid--default) 1. Take a look at some components [here](http://react-materialize.github.io/react-materialize/?path=/story/css-grid--default)

View file

@ -5,7 +5,7 @@
<link rel="icon" href="%PUBLIC_URL%/favicon.ico" /> <link rel="icon" href="%PUBLIC_URL%/favicon.ico" />
<meta name="viewport" content="width=device-width, initial-scale=1" /> <meta name="viewport" content="width=device-width, initial-scale=1" />
<meta name="theme-color" content="#ee6e73" /> <meta name="theme-color" content="#ee6e73" />
<meta name="description" content="Chef's | Meal Planner" /> <meta name="description" content="Online Meal Planner | Chef's" />
<link rel="apple-touch-icon" href="%PUBLIC_URL%/logo192.png" /> <link rel="apple-touch-icon" href="%PUBLIC_URL%/logo192.png" />
<link rel="manifest" href="%PUBLIC_URL%/manifest.json" /> <link rel="manifest" href="%PUBLIC_URL%/manifest.json" />
@ -28,15 +28,9 @@
href="https://fonts.googleapis.com/css?family=Marck+Script&display=swap" href="https://fonts.googleapis.com/css?family=Marck+Script&display=swap"
rel="stylesheet" rel="stylesheet"
/> />
<title>Chef's | Meal Planner</title> <title>Online Meal Planner | Chef's</title>
</head> </head>
<body> <body>
<!-- <script>
document.addEventListener("DOMContentLoaded", function() {
var elems = document.querySelectorAll(".sidenav");
var instances = M.Sidenav.init(elems, options);
});
</script> -->
<noscript>You need to enable JavaScript to run this app.</noscript> <noscript>You need to enable JavaScript to run this app.</noscript>
<div id="root"></div> <div id="root"></div>
</body> </body>

View file

@ -1,39 +1,43 @@
import React from "react"; import React from "react";
import { useInput } from "../utils/inputHook";
export const ContactForm = ({ handleSubmit }) => { export const ContactForm = ({ onSubmit }) => {
// const fields = ["firstname", "lastname", "email", "phone", "message"]; // const fields = ["firstname", "lastname", "email", "phone", "message"];
// const [firstName, setFirstName] = useState(""); // const [firstName, setFirstName] = useState("");
const onSubmit = ev => { const handleSubmit = ev => {
ev.preventDefault(); ev.preventDefault();
handleSubmit(true); onSubmit(true);
}; };
// const handleChange = ev => { // const handleChange = ev => {
// const { value } = ev.target; // const { value } = ev.target;
// setFirstName(value); // setFirstName(value);
// }; // };
// const { value, bind } = useInput("");
const { bind } = useInput("");
return ( return (
<form onSubmit={onSubmit}> <form onSubmit={handleSubmit}>
<div className="row"> <div className="row">
<div className="col s12"> <div className="col s12">
<div className="col s12 m6">
<ContactFormInput id="First Name" />
</div>
<div className="col s12 m6">
<ContactFormInput id="Last Name" />
</div>
<div className="col s12 m6"> <div className="col s12 m6">
<ContactFormInput <ContactFormInput
id="Email" id="First Name"
type="email" // value={firstName}
placeholder="jd@mail.com" // onChange={handleChange}
{...bind}
/> />
</div> </div>
<div className="col s12 m6"> <div className="col s12 m6">
<ContactFormInput id="Phone" placeholder="0123456789" /> <ContactFormInput id="Last Name" {...bind} />
</div>
<div className="col s12 m6">
<ContactFormInput id="Email" type="email" />
</div>
<div className="col s12 m6">
<ContactFormInput id="Phone" />
</div> </div>
<div className="col s12"> <div className="col s12">
<ContactFormTextArea id="Message" /> <ContactFormTextArea id="Message" />
@ -45,18 +49,18 @@ export const ContactForm = ({ handleSubmit }) => {
); );
}; };
const ContactFormInput = ({ id, type = "text" }) => { const ContactFormInput = ({ id, type = "text", value, onChange }) => {
return ( return (
<div className="input-field"> <div className="input-field">
{/* <i class="material-icons prefix">account_circle</i> */} {/* <i class="material-icons prefix">account_circle</i> */}
<label for={id}>{id}</label> <label htmlFor={id}>{id}</label>
<input <input
className="validate" className="validate"
type={type} type={type}
id={id} id={id}
name={id} name={value}
// value={value} value={value}
// onChange={onChange} onChange={onChange}
/> />
</div> </div>
); );
@ -65,7 +69,7 @@ const ContactFormInput = ({ id, type = "text" }) => {
const ContactFormTextArea = ({ id }) => { const ContactFormTextArea = ({ id }) => {
return ( return (
<div className="input-field"> <div className="input-field">
<label for={id}>{id}</label> <label htmlFor={id}>{id}</label>
<textarea <textarea
className="materialize-textarea validate" className="materialize-textarea validate"
rows="12" rows="12"
@ -85,7 +89,7 @@ const ContactFormSubmit = ({ text }) => {
type="submit" type="submit"
name="submit" name="submit"
> >
<i class="material-icons right">send</i> {text} <i className="material-icons right">send</i> {text}
</button> </button>
); );
}; };

View file

@ -9,8 +9,9 @@ import { LogOutButton } from "./LogOutButton";
export const Navbar = props => { export const Navbar = props => {
const { isAuthenticated } = useAuth0(); const { isAuthenticated } = useAuth0();
const links = ["categories", "contact"]; const links = ["categories", "contact"];
const { openNavClick } = props;
return ( return (
<div className="row"> <div className="navbar-fixed">
<nav> <nav>
<div className="nav-wrapper"> <div className="nav-wrapper">
<div className="container "> <div className="container ">
@ -29,50 +30,17 @@ export const Navbar = props => {
</li> </li>
<li>{!isAuthenticated ? <LogInButton /> : <LogOutButton />}</li> <li>{!isAuthenticated ? <LogInButton /> : <LogOutButton />}</li>
</ul> </ul>
<a
href="#"
data-target="slide-out"
class="sidenav-trigger "
onClick={openNavClick}
>
<i class="material-icons">menu</i>
</a>
</div> </div>
</div> </div>
</nav> </nav>
{/* <ul id="slide-out" class="sidenav show-on-small">
<li>
<div class="user-view">
<div class="background">
<img src="images/office.jpg" />
</div>
<a href="#user">
<img class="circle" src="images/yuna.jpg" />
</a>
<a href="#name">
<span class="white-text name">John Doe</span>
</a>
<a href="#email">
<span class="white-text email">jdandturk@gmail.com</span>
</a>
</div>
</li>
<li>
<a href="#!">
<i class="material-icons">cloud</i>First Link With Icon
</a>
</li>
<li>
<a href="#!">Second Link</a>
</li>
<li>
<div class="divider"></div>
</li>
<li>
<a class="subheader">Subheader</a>
</li>
<li>
<a class="waves-effect" href="#!">
Third Link With Waves
</a>
</li>
</ul>
<a href="#" data-target="slide-out" class="sidenav-trigger">
<i class="material-icons">menu</i>
</a> */}
</div> </div>
); );
}; };

View file

@ -4,28 +4,38 @@ import { Link } from "react-router-dom";
export const SearchBar = props => { export const SearchBar = props => {
return ( return (
<div className="container"> <div className="container">
<div className="row center-align"> <div className=" nav-wrapper">
<form> <div className="row center-align">
<input <form>
className="input-field col s10" <div className="input-field col s10">
type="text" <input
name="search" className=""
value={props.searchString} id="search"
placeholder="Search a recipe" type="search"
onChange={props.handleChange} required
/> name="search"
<Link to="/search"> value={props.searchString}
<button placeholder="Search for a recipe"
className="btn-floating waves-effect waves-light" onChange={props.handleChange}
type="submit" />
name="searchButton" <label className="label-icon" htmlFor="search">
value="Search" <i className="material-icons">search</i>
onClick={props.onSubmit} </label>
> <i className="material-icons">close</i>
<i className="material-icons right">send</i> </div>
</button> <Link to="/search">
</Link> <button
</form> className="btn-floating waves-effect waves-light"
type="submit"
name="searchButton"
value="Search"
onClick={props.onSubmit}
>
<i className="material-icons right">send</i>
</button>
</Link>
</form>
</div>
</div> </div>
</div> </div>
); );

View file

@ -0,0 +1,59 @@
import React, { useState } from "react";
import { Link } from "react-router-dom";
// import { useAuth0 } from "../utils/auth0-spa";
// import { Logo } from "./Logo";
// import { RandomButton } from "./RandomButton";
// import { FooterLink } from "./FooterLink";
// import { LogInButton } from "./LogInButton";
// import { LogOutButton } from "./LogOutButton";
export const SideNav = props => {
const { showNav } = props;
let transformStyle = {
transform: showNav ? "translateX(0%)" : "translateX(-105%)"
};
// let sideNavStyle = { width: showNav ? "250px" : "0" };
return (
<>
<ul id="slide-out" class="sidenav" style={transformStyle}>
<li>
<div class="user-view">
<div class="background">
<img src={require("../images/chef.svg")} />
</div>
<a href="#user">
<img class="circle" src={require("../images/chef.svg")} />
</a>
<a href="#name">
<span class="white-text name">John Doe</span>
</a>
<a href="#email">
<span class="white-text email">jdandturk@gmail.com</span>
</a>
</div>
</li>
<li>
<a href="#!">
<i class="material-icons">cloud</i>First Link With Icon
</a>
</li>
<li>
<a href="#!">Second Link</a>
</li>
<li>
<div class="divider"></div>
</li>
<li>
<a class="subheader">Subheader</a>
</li>
<li>
<a class="waves-effect" href="#!">
Third Link With Waves
</a>
</li>
</ul>
</>
);
};

View file

@ -1,7 +1,7 @@
import React from "react"; import React from "react";
import ReactDOM from "react-dom"; import ReactDOM from "react-dom";
import "./index.css"; import "./index.css";
import { App } from "./App"; import { App } from "./pages/App.jsx";
import * as serviceWorker from "./serviceWorker"; import * as serviceWorker from "./serviceWorker";
import { Auth0Provider } from "./utils/auth0-spa"; import { Auth0Provider } from "./utils/auth0-spa";
import history from "./utils/history"; import history from "./utils/history";

View file

@ -1,23 +1,24 @@
import React, { useState } from "react"; import React, { useState } from "react";
import { Router } from "./utils/router"; import { Router } from "../utils/router";
import { Switch, Route, Redirect } from "react-router-dom"; import { Switch, Route, Redirect } from "react-router-dom";
import { useAuth0 } from "./utils/auth0-spa"; import { useAuth0 } from "../utils/auth0-spa";
import { Home } from "./pages/Home"; import { Home } from "./Home";
import { Meal } from "./pages/Meal"; import { Meal } from "./Meal";
import { SearchPage } from "./pages/Search"; import { SearchPage } from "./Search";
import { CategoryListPage } from "./pages/CategoryList"; import { CategoryListPage } from "./CategoryList";
import { CategoryPage } from "./pages/Category"; import { CategoryPage } from "./Category";
import { ContactPage } from "./pages/Contact"; import { ContactPage } from "./Contact";
import { NotFoundPage } from "./pages/NotFound"; import { NotFoundPage } from "./NotFound";
import { Navbar } from "./components/Navbar"; import { Navbar } from "../components/Navbar";
import { SearchBar } from "./components/SearchBar"; import { SearchBar } from "../components/SearchBar";
import { Footer } from "./components/Footer"; import { Footer } from "../components/Footer";
import "./index.css"; import { getData } from "../utils/methods";
import { getData } from "./utils/methods"; import history from "../utils/history";
import history from "./utils/history"; import { Profile } from "./Profile";
import { Profile } from "./pages/Profile"; import { PrivateRoute } from "../components/PrivateRoute";
import { PrivateRoute } from "./components/PrivateRoute"; import { PreLoader } from "../components/PreLoader";
import { PreLoader } from "./components/PreLoader"; import { SideNav } from "../components/SideNav";
import "../index.css";
export const App = () => { export const App = () => {
const { loading } = useAuth0(); const { loading } = useAuth0();
@ -35,7 +36,7 @@ export const App = () => {
strArea: "Mine", strArea: "Mine",
strInstructions: strInstructions:
"Cook the pasta following pack instructions.\r\n\r\nHeat the oil in a non-stick frying pan and cook the onion, garlic and chilli for 3-4 mins to soften. Stir in the tomato pur\u00e9e and cook for 1 min, then add the pilchards with their sauce. Cook, breaking up the fish with a wooden spoon, then add the olives and continue to cook for a few more mins.\r\n\r\nDrain the pasta and add to the pan with 2-3 tbsp of the cooking water. Toss everything together well, then divide between plates and serve, scattered with Parmesan.", "Cook the pasta following pack instructions.\r\n\r\nHeat the oil in a non-stick frying pan and cook the onion, garlic and chilli for 3-4 mins to soften. Stir in the tomato pur\u00e9e and cook for 1 min, then add the pilchards with their sauce. Cook, breaking up the fish with a wooden spoon, then add the olives and continue to cook for a few more mins.\r\n\r\nDrain the pasta and add to the pan with 2-3 tbsp of the cooking water. Toss everything together well, then divide between plates and serve, scattered with Parmesan.",
strMealThumb: require("./images/breakfast.svg"), strMealThumb: require("../images/breakfast.svg"),
// "https://www.themealdb.com/images/media/meals/vvtvtr1511180578.jpg", // "https://www.themealdb.com/images/media/meals/vvtvtr1511180578.jpg",
strTags: null, strTags: null,
strYoutube: "#", strYoutube: "#",
@ -109,80 +110,108 @@ export const App = () => {
const buttonUrl = "/random"; const buttonUrl = "/random";
const [showNav, setShowNav] = useState(false);
const openNavClick = ev => {
ev.preventDefault();
setShowNav(true);
document.addEventListener("keydown", handleEscKey);
};
const closeNavClick = ev => {
ev.preventDefault();
setShowNav(false);
document.removeEventListener("keydown", handleEscKey);
};
const handleEscKey = ev => {
if (ev.key === "Escape") {
setShowNav(false);
}
};
return loading ? ( return loading ? (
<div className="container center-align valign-wrapper"> <div className="container center-align valign-wrapper">
<PreLoader /> <PreLoader />
</div> </div>
) : ( ) : (
<Router history={history}> <>
<header> <Router history={history}>
<Navbar handleClick={getRandomMeal} buttonUrl={buttonUrl} /> <header>
</header> <Navbar
<SearchBar handleClick={getRandomMeal}
searchString={searchString} buttonUrl={buttonUrl}
handleChange={handleChange} openNavClick={openNavClick}
onSubmit={getSearchResults}
/>
<Switch>
<Route exact path="/">
<Home buttonUrl={buttonUrl} />
</Route>
<PrivateRoute exact path="/profile">
<Profile />
</PrivateRoute>
<Route exact path={buttonUrl}>
{meal !== undefined && meal.meals !== null ? (
<Meal meal={meal} getMeal={getRandomMeal} />
) : (
<NotFoundPage handleClick={getRandomMeal} />
)}
</Route>
<Route exact path="/categories">
<CategoryListPage items={categories} getCategories={getCategories} />
</Route>
<Route path="/categories/:strCategory/">
<CategoryPage
getData={getData}
getMeal={getMeal}
setMeal={setMeal}
meal={meal}
/> />
</Route>
<Route exact path="/search"> <SearchBar
<SearchPage
searchString={searchString} searchString={searchString}
searchResults={searchResults} handleChange={handleChange}
onSubmit={getSearchResults}
/> />
</Route> <SideNav showNav={showNav} />
</header>
<Route path="/contact"> <Switch>
<ContactPage /> <Route exact path="/">
</Route> <Home buttonUrl={buttonUrl} />
</Route>
<Route path="/404"> <PrivateRoute exact path="/profile">
<NotFoundPage handleClick={getRandomMeal} /> <Profile />
</Route> </PrivateRoute>
<Route path="/:idMeal"> <Route exact path={buttonUrl}>
{meal !== undefined && meal.meals !== null ? ( {meal !== undefined && meal.meals !== null ? (
<Meal meal={meal} getMeal={getMeal} /> <Meal meal={meal} getMeal={getRandomMeal} />
) : ( ) : (
<NotFoundPage handleClick={getRandomMeal} />
)}
</Route>
<Route exact path="/categories">
<CategoryListPage
items={categories}
getCategories={getCategories}
/>
</Route>
<Route path="/categories/:strCategory/">
<CategoryPage
getData={getData}
getMeal={getMeal}
setMeal={setMeal}
meal={meal}
/>
</Route>
<Route exact path="/search">
<SearchPage
searchString={searchString}
searchResults={searchResults}
/>
</Route>
<Route path="/contact">
<ContactPage />
</Route>
<Route path="/404">
<NotFoundPage handleClick={getRandomMeal} /> <NotFoundPage handleClick={getRandomMeal} />
)} </Route>
</Route>
<Route path="*"> <Route path="/:idMeal">
<Redirect to="/404" /> {meal !== undefined && meal.meals !== null ? (
</Route> <Meal meal={meal} getMeal={getMeal} />
</Switch> ) : (
<NotFoundPage handleClick={getRandomMeal} />
)}
</Route>
<Footer /> <Route path="*">
</Router> <Redirect to="/404" />
</Route>
</Switch>
<Footer />
</Router>
</>
); );
}; };

View file

@ -16,8 +16,8 @@ export const ContactPage = props => {
</div> </div>
) : ( ) : (
<div className="container"> <div className="container">
<h1 className="logo">Contact Us</h1> <h2 className="logo">Contact Us</h2>
<ContactForm handleSubmit={setIsSubmitted} /> <ContactForm onSubmit={setIsSubmitted} />
</div> </div>
); );
}; };

17
src/utils/inputHook.js Normal file
View file

@ -0,0 +1,17 @@
import { useState } from "react";
export const useInput = initialValue => {
const [value, setValue] = useState(initialValue);
return {
value,
setValue,
reset: () => setValue(""),
bind: {
value,
onChange: ev => {
setValue(ev.target.value);
}
}
};
};