Compare commits
5 Commits
cf733097b8
...
d7ef59f2a6
Author | SHA1 | Date | |
---|---|---|---|
d7ef59f2a6 | |||
42813be6a6 | |||
4130321108 | |||
3896c1be0d | |||
3a2a44d828 |
@ -28,6 +28,7 @@
|
|||||||
</head>
|
</head>
|
||||||
<body>
|
<body>
|
||||||
<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-overlays"></div>
|
||||||
<div id="root"></div>
|
<div id="root"></div>
|
||||||
<!--
|
<!--
|
||||||
This HTML file is a template.
|
This HTML file is a template.
|
||||||
|
23
src/App.js
23
src/App.js
@ -1,16 +1,31 @@
|
|||||||
import React from "react";
|
import React, { useState } from "react";
|
||||||
|
|
||||||
import Header from "./components/Layout/Header";
|
import Header from "./components/Layout/Header";
|
||||||
import Meals from "./components/Meals/Meals";
|
import Meals from "./components/Meals/Meals";
|
||||||
|
import Cart from "./components/Cart/Cart";
|
||||||
|
import CartProvider from "./store/CartProvider";
|
||||||
|
|
||||||
const App = () => {
|
const App = () => {
|
||||||
|
const [cartIsShown, setCartIsShown] = useState(false);
|
||||||
|
|
||||||
|
const showCartHandler = () => {
|
||||||
|
setCartIsShown(true);
|
||||||
|
};
|
||||||
|
|
||||||
|
const hideCartHandler = () => {
|
||||||
|
setCartIsShown(false);
|
||||||
|
};
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<>
|
<CartProvider>
|
||||||
<Header />
|
{cartIsShown && (
|
||||||
|
<Cart onShowCart={showCartHandler} onHideCart={hideCartHandler} />
|
||||||
|
)}
|
||||||
|
<Header onShowCart={showCartHandler} />
|
||||||
<main>
|
<main>
|
||||||
<Meals />
|
<Meals />
|
||||||
</main>
|
</main>
|
||||||
</>
|
</CartProvider>
|
||||||
);
|
);
|
||||||
};
|
};
|
||||||
|
|
||||||
|
30
src/components/Cart/Cart.js
Normal file
30
src/components/Cart/Cart.js
Normal file
@ -0,0 +1,30 @@
|
|||||||
|
import classes from "./Cart.module.css";
|
||||||
|
import Modal from "../UI/Modal";
|
||||||
|
|
||||||
|
const Cart = (props) => {
|
||||||
|
const cartItems = (
|
||||||
|
<ul className={classes["cart-items"]}>
|
||||||
|
{[{ id: "c1", name: "sushi", amount: 2, price: 12.99 }].map((item) => (
|
||||||
|
<li>{item.name}</li>
|
||||||
|
))}
|
||||||
|
</ul>
|
||||||
|
);
|
||||||
|
|
||||||
|
return (
|
||||||
|
<Modal onShowCart={props.onShowCart} onHideCart={props.onHideCart}>
|
||||||
|
{cartItems}
|
||||||
|
<div className={classes.total}>
|
||||||
|
<span>Total Amount</span>
|
||||||
|
<span>35.63</span>
|
||||||
|
</div>
|
||||||
|
<div className={classes.actions}>
|
||||||
|
<button className={classes["button--alt"]} onClick={props.onHideCart}>
|
||||||
|
Close
|
||||||
|
</button>
|
||||||
|
<button className={classes.button}>Order</button>
|
||||||
|
</div>
|
||||||
|
</Modal>
|
||||||
|
);
|
||||||
|
};
|
||||||
|
|
||||||
|
export default Cart;
|
46
src/components/Cart/Cart.module.css
Normal file
46
src/components/Cart/Cart.module.css
Normal file
@ -0,0 +1,46 @@
|
|||||||
|
.cart-items {
|
||||||
|
list-style: none;
|
||||||
|
margin: 0;
|
||||||
|
padding: 0;
|
||||||
|
max-height: 20rem;
|
||||||
|
overflow: auto;
|
||||||
|
}
|
||||||
|
|
||||||
|
.total {
|
||||||
|
display: flex;
|
||||||
|
justify-content: space-between;
|
||||||
|
align-items: center;
|
||||||
|
font-weight: bold;
|
||||||
|
font-size: 1.5rem;
|
||||||
|
margin: 1rem 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
.actions {
|
||||||
|
text-align: right;
|
||||||
|
}
|
||||||
|
|
||||||
|
.actions button {
|
||||||
|
font: inherit;
|
||||||
|
cursor: pointer;
|
||||||
|
background-color: transparent;
|
||||||
|
border: 1px solid #8a2b06;
|
||||||
|
padding: 0.5rem 2rem;
|
||||||
|
border-radius: 25px;
|
||||||
|
margin-left: 1rem;
|
||||||
|
}
|
||||||
|
|
||||||
|
.actions button:hover,
|
||||||
|
.actions button:active {
|
||||||
|
background-color: #5a1a01;
|
||||||
|
border-color: #5a1a01;
|
||||||
|
color: white;
|
||||||
|
}
|
||||||
|
|
||||||
|
.actions .button--alt {
|
||||||
|
color: #8a2b06;
|
||||||
|
}
|
||||||
|
|
||||||
|
.actions .button {
|
||||||
|
background-color: #8a2b06;
|
||||||
|
color: white;
|
||||||
|
}
|
@ -4,12 +4,12 @@ import mealsImage from "../../assets/meals.jpg";
|
|||||||
import classes from "./Header.module.css";
|
import classes from "./Header.module.css";
|
||||||
import HeaderCartButton from "./HeaderCartButton";
|
import HeaderCartButton from "./HeaderCartButton";
|
||||||
|
|
||||||
const Header = () => {
|
const Header = (props) => {
|
||||||
return (
|
return (
|
||||||
<>
|
<>
|
||||||
<header className={classes.header}>
|
<header className={classes.header}>
|
||||||
<h1>ReactMeals</h1>
|
<h1>ReactMeals</h1>
|
||||||
<HeaderCartButton />
|
<HeaderCartButton onShowCart={props.onShowCart} />
|
||||||
</header>
|
</header>
|
||||||
<div className={classes["main-image"]}>
|
<div className={classes["main-image"]}>
|
||||||
<img src={mealsImage} alt="A table full of delicious food!" />
|
<img src={mealsImage} alt="A table full of delicious food!" />
|
||||||
|
@ -1,14 +1,22 @@
|
|||||||
import CartIcon from "../Cart/CartIcon";
|
import CartIcon from "../Cart/CartIcon";
|
||||||
import classes from "./HeaderCartButton.module.css";
|
import classes from "./HeaderCartButton.module.css";
|
||||||
|
import CartContext from "../../store/cart-context";
|
||||||
|
import { useContext } from "react";
|
||||||
|
|
||||||
const HeaderCartButton = (props) => {
|
const HeaderCartButton = (props) => {
|
||||||
|
const cartCtx = useContext(CartContext);
|
||||||
|
|
||||||
|
const numberOfCartItems = cartCtx.items.reduce((curNumber, item) => {
|
||||||
|
return curNumber + item.amount;
|
||||||
|
}, 0);
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<button className={classes.button}>
|
<button className={classes.button} onClick={props.onShowCart}>
|
||||||
<span className={classes.icon}>
|
<span className={classes.icon}>
|
||||||
<CartIcon />
|
<CartIcon />
|
||||||
</span>
|
</span>
|
||||||
<span>Your Cart</span>
|
<span>Your Cart</span>
|
||||||
<span className={classes.badge}>3</span>
|
<span className={classes.badge}>{numberOfCartItems}</span>
|
||||||
</button>
|
</button>
|
||||||
);
|
);
|
||||||
};
|
};
|
||||||
|
33
src/components/UI/Modal.js
Normal file
33
src/components/UI/Modal.js
Normal file
@ -0,0 +1,33 @@
|
|||||||
|
import classes from "./Modal.module.css";
|
||||||
|
import ReactDOM from "react-dom";
|
||||||
|
|
||||||
|
const Backdrop = (props) => {
|
||||||
|
return <div className={classes.backdrop} onClick={props.onHideCart}></div>;
|
||||||
|
};
|
||||||
|
|
||||||
|
const ModalOverlay = (props) => {
|
||||||
|
return (
|
||||||
|
<div className={classes.modal}>
|
||||||
|
<div className={classes.content}>{props.children}</div>
|
||||||
|
</div>
|
||||||
|
);
|
||||||
|
};
|
||||||
|
|
||||||
|
const portalElement = document.getElementById("root-overlays");
|
||||||
|
|
||||||
|
const Modal = (props) => {
|
||||||
|
return (
|
||||||
|
<>
|
||||||
|
{ReactDOM.createPortal(
|
||||||
|
<Backdrop onHideCart={props.onHideCart} />,
|
||||||
|
portalElement
|
||||||
|
)}
|
||||||
|
{ReactDOM.createPortal(
|
||||||
|
<ModalOverlay>{props.children}</ModalOverlay>,
|
||||||
|
portalElement
|
||||||
|
)}
|
||||||
|
</>
|
||||||
|
);
|
||||||
|
};
|
||||||
|
|
||||||
|
export default Modal;
|
40
src/components/UI/Modal.module.css
Normal file
40
src/components/UI/Modal.module.css
Normal file
@ -0,0 +1,40 @@
|
|||||||
|
.backdrop {
|
||||||
|
position: fixed;
|
||||||
|
top: 0;
|
||||||
|
left: 0;
|
||||||
|
width: 100%;
|
||||||
|
height: 100vh;
|
||||||
|
z-index: 20;
|
||||||
|
background-color: rgba(0, 0, 0, 0.75);
|
||||||
|
}
|
||||||
|
|
||||||
|
.modal {
|
||||||
|
position: fixed;
|
||||||
|
top: 20vh;
|
||||||
|
left: 5%;
|
||||||
|
width: 90%;
|
||||||
|
background-color: white;
|
||||||
|
padding: 1rem;
|
||||||
|
border-radius: 14px;
|
||||||
|
box-shadow: 0 2px 8px rgba(0, 0, 0, 0.25);
|
||||||
|
z-index: 30;
|
||||||
|
animation: slide-down 300ms ease-out forwards;
|
||||||
|
}
|
||||||
|
|
||||||
|
@media (min-width: 768px) {
|
||||||
|
.modal {
|
||||||
|
width: 40rem;
|
||||||
|
left: calc(50% - 20rem);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@keyframes slide-down {
|
||||||
|
from {
|
||||||
|
opacity: 0;
|
||||||
|
transform: translateY(-3rem);
|
||||||
|
}
|
||||||
|
to {
|
||||||
|
opacity: 1;
|
||||||
|
transform: translateY(0);
|
||||||
|
}
|
||||||
|
}
|
21
src/store/CartProvider.js
Normal file
21
src/store/CartProvider.js
Normal file
@ -0,0 +1,21 @@
|
|||||||
|
import CartContext from "./cart-context";
|
||||||
|
|
||||||
|
const CartProvider = (props) => {
|
||||||
|
const addItemToCartHandler = (item) => {};
|
||||||
|
const removeItemFromCartHandler = (id) => {};
|
||||||
|
|
||||||
|
const cartContext = {
|
||||||
|
items: [],
|
||||||
|
totalAmount: 0,
|
||||||
|
addItem: addItemToCartHandler,
|
||||||
|
removeItem: removeItemFromCartHandler,
|
||||||
|
};
|
||||||
|
|
||||||
|
return (
|
||||||
|
<CartContext.Provider value={cartContext}>
|
||||||
|
{props.children}
|
||||||
|
</CartContext.Provider>
|
||||||
|
);
|
||||||
|
};
|
||||||
|
|
||||||
|
export default CartProvider;
|
10
src/store/cart-context.js
Normal file
10
src/store/cart-context.js
Normal file
@ -0,0 +1,10 @@
|
|||||||
|
import React from "react";
|
||||||
|
|
||||||
|
const CartContext = React.createContext({
|
||||||
|
items: [],
|
||||||
|
totalAmount: 0,
|
||||||
|
addItem: (item) => {},
|
||||||
|
removeItem: (id) => {},
|
||||||
|
});
|
||||||
|
|
||||||
|
export default CartContext;
|
Loading…
Reference in New Issue
Block a user