UIの話。
商品一覧ページの各商品ごとの説明ページ(Proiducts.js)及びそれをクリックしたときの詳細ページ(Details.js)、マウスホバーしたときに出るボタンをクリックすることで表示されるモーダル(Modals.js)の説明を行う。
商品説明の済に出てくるボタンをクリックすることで商品の画像のモーダルが表示される。
react-phone-e-commerce-project/src/components/Products.js
| import React, { Component } from "react"; |
|:--|
| import styled from "styled-components"; |
| import { Link } from "react-router-dom"; |
| import { ProductConsumer } from "../context"; |
| export default class Product extends Component { |
| render() { |
| const { id, title, img, price, inCart } = this.props.product; |
stateとpropsの違いについて
https://qiita.com/kyrieleison/items/78b3295ff3f37969ab50
| return ( |
| <ProductWrapper className="col-9 mx-auto col-md-6 col-lg-3 my-3"> |
| <div className="card"> |
| <ProductConsumer> |
| {value => { |
| return ( |
| <div |
| className="img-container p-5" |
| onClick={() => value.handleDetail(id)} |
| > |
| <Link to="/details"> |
| <img src={img} alt="" className="card-img-top" /> |
| </Link> |
| <button |
| className="cart-btn" |
| disabled={inCart ? true : false} |
| onClick={() => { |
| value.addToCart(id); |
| value.openModal(id); |
| }} |
| > |
| {inCart ? ( |
| <p className="text-capitalize mb-0" disabled> |
| in cart |
| </p> |
| ) : ( |
| <i className="fas fa-cart-plus" /> |
| )} |
| </button> |
| </div> |
| ); |
| }} |
| </ProductConsumer> |
| <div className="card-footer d-flex justify-content-between"> |
| <p className="align-self-center mb-0">{title}</p> |
| <h5 className="text-blue font-italic mb-0"> |
| <span className="mr-1">$</span> |
| {price} |
| </h5> |
| </div> |
| </div> |
| </ProductWrapper> |
| ); |
| } |
| } |
| |
| const ProductWrapper = styled.div` |
| .card { |
| border-color: transparent; |
| transition: all 1s linear; |
| } |
| .card-footer { |
| background: transparent; |
| border-top: transparent; |
| transition: all 1s linear; |
| } |
| &:hover { |
| .card { |
| border: 0.04rem solid rgba(0, 0, 0, 0.2); |
| box-shadow: 2px 2px 5px 0px rgba(0, 0, 0, 0.2); |
| } |
| .card-footer { |
| background: rgba(247, 247, 247); |
| } |
| } |
| .img-container { |
| position: relative; |
| overflow: hidden; |
| } |
| .card-img-top { |
| transition: all 1s linear; |
| } |
| .img-container:hover .card-img-top { |
| transform: scale(1.2); |
| } |
| .cart-btn { |
| position: absolute; |
| bottom: 0; |
| right: 0; |
| padding: 0.2rem 0.4rem; |
| background: var(--lightBlue); |
| border: none; |
| color: var(--mainWhite); |
| font-size: 1.4rem; |
| border-radius: 0.5rem 0 0 0; |
| transform: translate(100%, 100%); |
| transition: all 1s ease-in-out; |
| } |
| .img-container:hover .cart-btn { |
| transform: translate(0, 0); |
| } |
| .cart-btn:hover { |
| color: var(--mainBlue); |
| cursor: pointer; |
| } |
| `; |
react-phone-e-commerce-project/src/components/Modals.jsにおいて
| import React, { Component } from "react"; |
|:--|
| import { ProductConsumer } from "../context"; |
| import { ButtonContainer } from "./Button"; |
| import { Link } from "react-router-dom"; |
| export default class Details extends Component { |
| render() { |
| return ( |
| <ProductConsumer> |
| {value => { |
| const { |
| id, |
| company, |
| img, |
| info, |
| price, |
| title, |
| inCart |
| } = value.detailProduct; |
データの格納を行う。
| return ( |
| <div className="container py-5"> |
| {/* title */} |
| <div className="row"> |
| <div className="col-10 mx-auto text-center text-slanted text-blue my-5"> |
| <h1>{title}</h1> |
| </div> |
| </div> |
| {/* end of title */} |
| <div className="row"> |
| <div className="col-10 mx-auto col-md-6 my-3"> |
| <img src={img} className="img-fluid" alt="" /> |
| </div> |
| {/* prdoduct info */} |
| <div className="col-10 mx-auto col-md-6 my-3 text-capitalize"> |
| <h1>model : {title}</h1> |
| <h4 className="text-title text-uppercase text-muted mt-3 mb-2"> |
| made by : <span className="text-uppercase">{company}</span> |
| </h4> |
| <h4 className="text-blue"> |
| <strong> |
| price : <span>$</span> |
| {price} |
| </strong> |
| </h4> |
| <p className="text-capitalize font-weight-bold mt-3 mb-0"> |
| some info about product : |
| </p> |
| <p className="text-muted lead">{info}</p> |
| {/* buttons */} |
| <div> |
| <Link to="/"> |
| <ButtonContainer>back to products</ButtonContainer> |
| </Link> |
| <ButtonContainer |
| cart |
| disabled={inCart ? true : false} |
| onClick={() => { |
| value.addToCart(id); |
| value.openModal(id); |
| }} |
| > |
| {inCart ? "in cart" : "add to cart"} |
{inCart ? "in cart" : "add to cart"}
カートの中にあったら"in cart"カートの中に入っていますと伝える
カートの中に入ってなかったら"add to cart"入っていますと伝える。
| </ButtonContainer> |
| </div> |
| </div> |
| </div> |
| </div> |
| ); |
| }} |
| </ProductConsumer> |
| ); |
| } |
| } |
このファイルは終了
次はモーダル
react-phone-e-commerce-project/src/components/Modal.js
| import React, { Component } from "react"; |
|:--|
| import styled from "styled-components"; |
このstyled-componentsは
https://qiita.com/taneba/items/4547830b461d11a69a20
参照
| import { ProductConsumer } from "../context"; |
| import { ButtonContainer } from "./Button"; |
| import { Link } from "react-router-dom"; |
| export default class Modal extends Component { |
| render() { |
| return ( |
| <ProductConsumer> |
はsrc/context.jsから呼び出しているが、src/context.jsに宣言しているだけで中身はない。
呼び出して肉付けしている。
というイメージ。
| {value => { |
| const { modalOpen, closeModal } = value; |
| const { img, title, price } = value.modalProduct; |
| if (!modalOpen) { |
| return null; |
| } else { |
| return ( |
| <ModalContainer> |
| <div className="container"> |
| <div className="row"> |
| <div |
| className="col-8 mx-auto col-md-6 col-lg-4 p-5 text-center text-capitalize" |
| id="modal" |
| > |
| <h5>item added to cart</h5> |
| <img src={img} className="img-fluid" alt="" /> |
| <h5>{title}</h5> |
| <h5 className="text-muted">price : ${price}</h5> |
| <Link to="/"> |
| <ButtonContainer |
| onClick={() => { |
| closeModal(); |
| }} |
| > |
| Continue Shopping |
| </ButtonContainer> |
| </Link> |
| <Link to="/cart"> |
| <ButtonContainer |
| cart |
| onClick={() => { |
| closeModal(); |
| }} |
| > |
カート情報のページに映るとしても、商品ページにとどまるにしても一回モーダルは閉じなければならない。
| Go To Cart |
| </ButtonContainer> |
| </Link> |
| </div> |
| </div> |
| </div> |
| </ModalContainer> |
| ); |
| } |
| }} |
| </ProductConsumer> |
| ); |
| } |
| } |
| |
| const ModalContainer = styled.div` |
| position: fixed; |
| top: 0; |
| left: 0; |
| right: 0; |
| bottom: 0; |
| background: rgba(0, 0, 0, 0.3); |
| display: flex; |
| align-items: center; |
| justify-content: center; |
| #modal { |
| background: var(--mainWhite); |
| } |
| `;
Modalのcss設計