Compare commits
5 Commits
cf733097b8
...
d7ef59f2a6
Author | SHA1 | Date | |
---|---|---|---|
d7ef59f2a6 | |||
42813be6a6 | |||
4130321108 | |||
3896c1be0d | |||
3a2a44d828 |
@ -28,6 +28,7 @@
|
||||
</head>
|
||||
<body>
|
||||
<noscript>You need to enable JavaScript to run this app.</noscript>
|
||||
<div id="root-overlays"></div>
|
||||
<div id="root"></div>
|
||||
<!--
|
||||
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 Meals from "./components/Meals/Meals";
|
||||
import Cart from "./components/Cart/Cart";
|
||||
import CartProvider from "./store/CartProvider";
|
||||
|
||||
const App = () => {
|
||||
const [cartIsShown, setCartIsShown] = useState(false);
|
||||
|
||||
const showCartHandler = () => {
|
||||
setCartIsShown(true);
|
||||
};
|
||||
|
||||
const hideCartHandler = () => {
|
||||
setCartIsShown(false);
|
||||
};
|
||||
|
||||
return (
|
||||
<>
|
||||
<Header />
|
||||
<CartProvider>
|
||||
{cartIsShown && (
|
||||
<Cart onShowCart={showCartHandler} onHideCart={hideCartHandler} />
|
||||
)}
|
||||
<Header onShowCart={showCartHandler} />
|
||||
<main>
|
||||
<Meals />
|
||||
</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 HeaderCartButton from "./HeaderCartButton";
|
||||
|
||||
const Header = () => {
|
||||
const Header = (props) => {
|
||||
return (
|
||||
<>
|
||||
<header className={classes.header}>
|
||||
<h1>ReactMeals</h1>
|
||||
<HeaderCartButton />
|
||||
<HeaderCartButton onShowCart={props.onShowCart} />
|
||||
</header>
|
||||
<div className={classes["main-image"]}>
|
||||
<img src={mealsImage} alt="A table full of delicious food!" />
|
||||
|
@ -1,14 +1,22 @@
|
||||
import CartIcon from "../Cart/CartIcon";
|
||||
import classes from "./HeaderCartButton.module.css";
|
||||
import CartContext from "../../store/cart-context";
|
||||
import { useContext } from "react";
|
||||
|
||||
const HeaderCartButton = (props) => {
|
||||
const cartCtx = useContext(CartContext);
|
||||
|
||||
const numberOfCartItems = cartCtx.items.reduce((curNumber, item) => {
|
||||
return curNumber + item.amount;
|
||||
}, 0);
|
||||
|
||||
return (
|
||||
<button className={classes.button}>
|
||||
<button className={classes.button} onClick={props.onShowCart}>
|
||||
<span className={classes.icon}>
|
||||
<CartIcon />
|
||||
</span>
|
||||
<span>Your Cart</span>
|
||||
<span className={classes.badge}>3</span>
|
||||
<span className={classes.badge}>{numberOfCartItems}</span>
|
||||
</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