<div id="app">
</div>
* {
box-sizing: border-box;
margin: 0;
padding: 0;
}
h1, p {
font-family: Lato;
}
.container {
position: relative;
width: 100%;
overflow: hidden;
}
nav {
position: fixed;
width: 100%;
height: 40px;
display: flex;
justify-content: center;
align-items: center;
background-color: crimson;
}
nav > span {
cursor: pointer;
color: #fff;
margin: 0 15px;
}
.rotateBasket {
animation-name: rotateBasket;
animation-duration: 2s;
animation-delay: 0s;
animation-direction: normal;
animation-iteration-count: 2;
animation-fill-mode: forwards;
animation-timing-function: linear;
}
@keyframes rotateBasket {
0% {
transform: rotate(0deg);
}
20% {
transform: rotate(45deg);
}
40% {
transform: rotate(-45deg);
}
60% {
transform: rotate(45deg);
}
80% {
transform: rotate(-45deg);
}
100% {
transform: rotate(0deg);
}
}
.comments {
margin: 40px auto;
display: flex;
flex-direction: column;
justify-content: flex-start;
max-width: 1200px;
}
.comments > h1 {
align-self: center;
color: crimson;
margin: 20px;
}
.comments > input {
align-self: center;
border: 1px solid lightgray;
padding: 10px;
box-shadow: 1px 3px 5px -7px rgb(221, 208, 208);
width: 400px;
margin-bottom: 20px;
}
.comments > span {
align-self: center;
color: crimson;
margin-bottom: 15px;
cursor: pointer;
}
.comments-wrapper {
align-self: center;
width: 400px;
margin: 10px auto;
display: flex;
flex-direction: column;
}
.comments-wrapper p {
display: inline-flex;
width: 100%;
flex-direction: row;
justify-content: flex-start;
align-items: center;
color: dimgray;
margin: auto 5px;
padding: 20px;
border-radius: 7px;
border: 1px solid rgba(214, 192, 192, 0.808);
}
@media all and (max-width: 450px){
.input, .comments-wrapper {
width: 100%;
margin: 0 10px;
}
.comments-wrapper p {
margin: 0;
}
}
.cats-all {
margin: 40px auto;
overflow: hidden;
display: flex;
justify-content: flex-start;
align-items: flex-start;
flex-wrap: wrap;
max-width: 1200px;
}
.single-cat {
overflow: hidden;
height: 550px;
width: 30.333%;
margin: 10px 1.5%;
display: inline-flex;
flex-direction: column;
border: 1px solid rgba(241, 235, 235, 0.452);
border-radius: 7px;
box-shadow: 1px 3px 5px -7px #000;
}
.single-cat > h3 {
align-self: flex-start;
color: crimson;
font-size: 1.4em;
margin: 20px;
}
.single-cat > img {
margin: 0 auto;
height: 340px;
}
.single-cat > p{
padding: 20px;
}
.single-cat > span {
padding: 20px;
color: crimson;
font-size: 1.2em;
width: 30px;
height: 30px;
align-self: flex-start;
margin: 20px;
display: inline-flex;
border-radius: 50%;
justify-content: center;
align-items: center;
background-color: rgba(211, 205, 205, 0.651);
}
.single-cat > span > i {
cursor: pointer;
}
@media all and (max-width: 960px){
.single-cat {
width: 45%;
margin: 10px 2.5%;
}
}
@media all and (max-width: 680px){
.single-cat {
width: 100%;
}
}
.cart {
margin: 40px auto;
max-width: 1200px;
}
.items-wrapper {
overflow: hidden;
display: flex;
justify-content: flex-start;
align-items: flex-start;
flex-wrap: wrap;
}
.items-wrapper > .single-cat {
overflow: hidden;
height: 550px;
width: 30.333%;
margin: 10px 1.5%;
display: inline-flex;
flex-direction: column;
border: 1px solid rgba(241, 235, 235, 0.452);
border-radius: 7px;
box-shadow: 1px 3px 5px -7px #000;
}
.items-wrapper > .single-cat > h3 {
align-self: flex-start;
color: crimson;
font-size: 1.4em;
margin: 20px;
}
.items-wrapper > .single-cat > img {
margin: 0 auto;
height: 340px;
}
.single-cat > p{
padding: 20px;
}
.single-cat > span {
padding: 20px;
color: crimson;
font-size: 1.2em;
width: 30px;
height: 30px;
align-self: flex-start;
margin: 20px;
display: inline-flex;
border-radius: 50%;
justify-content: center;
align-items: center;
background-color: rgba(211, 205, 205, 0.651);
}
.items-wrapper > .single-cat > span > i {
cursor: pointer;
}
.cart > h3 {
margin: 50px 20px;
}
@media all and (max-width: 960px){
.single-cat {
width: 45%;
margin: 10px 2.5%;
}
}
@media all and (max-width: 680px){
.single-cat {
width: 100%;
margin: 0;
}
}
let cats = [
{
id: "1",
name: "cat 1",
description: "Cat description goes here",
price: "12.99",
src:
"https://raw.githubusercontent.com/ivanmmarkovic/misc/master/images/cats-images-for-project/adorable-animal-blur-cat-617278.jpg",
count: "3"
},
{
id: "2",
name: "cat 2",
description: "Cat description goes here",
price: "22.99",
src:
"https://raw.githubusercontent.com/ivanmmarkovic/misc/master/images/cats-images-for-project/animal-cat-face-close-up-feline-416160.jpg",
count: "2"
},
{
id: "3",
name: "cat 3",
description: "Cat description goes here",
price: "17.99",
src:
"https://raw.githubusercontent.com/ivanmmarkovic/misc/master/images/cats-images-for-project/adorable-animal-cat-302280.jpg",
count: "5"
},
{
id: "4",
name: "cat 4",
description: "Cat description goes here",
price: "27.99",
src:
"https://raw.githubusercontent.com/ivanmmarkovic/misc/master/images/cats-images-for-project/adorable-animal-baby-blur-177809.jpg",
count: "3"
}
];
let comments = ["Do you have a cat with a hat?", "Nice"];
class App extends React.Component {
constructor(props) {
super();
this.state = {
cats: cats,
cart: [],
display: {
cats: true,
cart: false,
comments: false
},
comments: comments,
cartLinkClassName: "material-icons"
}
}
addToCart(id) {
console.log("Added :", id);
let cats = this.state.cats;
let cart = this.state.cart;
for (let i = 0; i < cats.length; i++) {
if (cats[i].id == id && cats[i].count > 0) {
if (cart.length === 0) {
cats[i].count = cats[i].count - 1;
let cat = Object.assign({}, cats[i]);
delete(cat.count);
cat.howMany = typeof cat.howMany === "undefined" ? 1 : cat.howMany + 1;
cart.push(cat);
} else {
let result = cart.filter((item) => item.id == cats[i].id);
if (result.length === 1) {
for (let j = 0; j < cart.length; j++) {
if (cart[j].id == cats[i].id) {
cats[i].count = cats[i].count - 1;
cart[j].howMany++;
}
}
} else {
cats[i].count--;
let cat = Object.assign({}, cats[i]);
delete(cat.count);
cat.howMany = typeof cat.howMany === "undefined" ? 1 : cat.howMany + 1;
cart.push(cat);
}
}
}
}
this.setState({
cats: cats,
cart: cart,
cartLinkClassName: "material-icons rotateBasket"
});
setTimeout(() => this.setState({
cartLinkClassName: "material-icons"
}), 2000);
}
removeItem(id) {
let cats = this.state.cats;
let cart = this.state.cart;
let itemsToGetBack = 0;
for (let i = 0; i < cart.length; i++) {
if (cart[i].id == id) {
let cartIndex = i;
itemsToGetBack = cart[i].howMany;
cart.splice(cartIndex, 1);
}
}
for (let i = 0; i < cats.length; i++) {
if (cats[i].id == id) {
cats[i].count = cats[i].count + itemsToGetBack;
}
}
this.setState({
cats: cats,
cart: cart
})
}
displayThisComponent(comp) {
switch (comp) {
case "cats":
this.setState({
display: {
cats: true,
cart: false,
comments: false
}
});
break;
case "cart":
this.setState({
display: {
cats: false,
cart: true,
comments: false
}
});
break;
case "comments":
this.setState({
display: {
cats: false,
cart: false,
comments: true
}
});
break;
}
}
addComment(comment) {
let comments = this.state.comments;
comments.push(comment);
this.setState({
comments: comments
})
}
render() {
let cats = this.state.display.cats ? <Cats cats={this.state.cats} addToCart={this.addToCart.bind(this)} /> : "";
let cart = this.state.display.cart ? <Cart cart={this.state.cart} removeItem={this.removeItem.bind(this)}/> : "";
let comments = this.state.display.comments ? <Comments comments={this.state.comments} addComment={this.addComment.bind(this)}/> : "";
return (
<div className="container">
<nav>
<span onClick={() => this.displayThisComponent("cats")}>
<i className="material-icons">store</i>
</span>
<span onClick={() => this.displayThisComponent("cart")}>
<i className={this.state.cartLinkClassName}>add_shopping_cart</i>
</span>
<span onClick={() => this.displayThisComponent("comments")}>
<i className="material-icons">chat_bubble</i>
</span>
</nav>
{cats}
{cart}
{comments}
</div>
);
}
}
class Cats extends React.Component {
render() {
var addToCart = this.props.addToCart;
var catsNodes = this.props.cats.map((cat, i) => <Cat key={i} cat={cat} addToCart={addToCart} />);
return (
<div className="cats-all" >
{catsNodes}
</div>
);
}
};
class Cat extends React.Component {
render() {
return (
<div className="single-cat">
<h3>{this.props.cat.name}</h3>
<img src={this.props.cat.src} />
<p><b>Price</b> {this.props.cat.price} | {this.props.cat.count} {" "}available</p>
<span onClick={() => this.props.addToCart(this.props.cat.id)}><i className="material-icons">add_shopping_cart</i></span>
</div>
)
}
};
class Cart extends React.Component {
removeItem(id) {
this.props.removeItem(id);
}
render() {
const removeItem = this.removeItem.bind(this);
let totalPrice = 0;
for (let i = 0; i < this.props.cart.length; i++) {
totalPrice = totalPrice + (this.props.cart[i].price * this.props.cart[i].howMany);
}
totalPrice = totalPrice.toFixed(2);
let itemsNodes = this.props.cart.map((item, i) => {
let singleCatTotal = (item.price * item.howMany).toFixed(2);
return (
<div key={i} className="single-cat">
<h3>{item.name}</h3>
<img src={item.src} />
<p>{item.description}</p>
<p>Items {item.howMany} | price {item.price} | total {singleCatTotal}</p>
<span onClick={() => removeItem(item.id)}><i className="material-icons">delete</i></span>
</div>
)
});
return (
<div className="cart">
<div className="items-wraper">
{itemsNodes}
</div>
<h3>Total : {totalPrice}</h3>
</div>
);
}
};
class Comments extends React.Component {
constructor(props) {
super();
this.state = {
comment: ""
}
}
getComment(event) {
this.setState({
comment: event.target.value
});
}
addComment() {
if (this.state.comment != "") {
this.props.addComment(this.state.comment);
}
this.setState({
comment: ""
})
}
render() {
let commentsNodes = this.props.comments.map((comment, i) => <div key={i}><p>{comment}</p></div>);
return (
<div className="comments">
<h1>Comments</h1>
<input type="text" onChange={this.getComment.bind(this)} value={this.state.comment}/>
<span><i className="material-icons" onClick={this.addComment.bind(this)}>message</i></span>
<div className="comments-wrapper">
{commentsNodes}
</div>
</div>
)
}
};
ReactDOM.render(
<App cats={cats}/>,
document.getElementById("app")
);
View Compiled
This Pen doesn't use any external CSS resources.