platzi / react-practico

65 stars 118 forks source link

Reto #6: mobile menu visibility #6

Open juandc opened 3 years ago

juandc commented 3 years ago

Ciao! Non abbiamo un menu mobile! 🇮🇹 🙀 🍕

¡No tenemos menú en mobile! ¡Debes agregarlo!

Recuerda que en el curso práctico de frontend developer creamos dos componentes diferentes del menú, uno para mobile y otro para desktop.

Ya logramos que el menú desktop aparezca con interacciones de los usuarios. Ahora debes encargarte de que el menú de la versión mobile aparezca y desaparezca cuando los usuarios presionen el ícono de menú en la navbar.

Para esto vas a requerir agregar nueva lógica en JavaScript y React.js, además de terminar de ajustar el CSS del menú.

Menú mobile

Menú mobile

wldrocha commented 2 years ago

Cree el componente del menu lateral, adicional lo importe en el componente Header, cree otro estado llamado toggleMobileMenu, a su vez en la imagen del menu en mobile le añadí un evento click y lo añadí encima de los otros 2 elementos modales, adicionalmente coloque en los estilos una posición absoluta, left de 0 para que flotará del lado izquierdo y le añadí un fondo blanco

christian-olivers commented 2 years ago

mi solución quedo de la siguiente manera en header.jsx: ... const Header = () => { ... const [menuToggle, setMenuToggle] = useState(false);

 const menuLeftToggle = () => {
    setMenuToggle(!menuToggle);
    console.log("entra")
}
return (
    <nav>
        <img src={menu} alt="menu" className="menu" onClick={menuLeftToggle}/>
         ...
         <ul className={'menu-left' + (menuToggle ? '--active':'')}>
               ...
               <div className={menuToggle ? 'settings-user' : ''}>
                          <div className='line'></div>
                          <a href="/">My orders</a>
                          <a href="/">My account</a>
                          <div className='user'>
                              <a className='user-email'>platzi@example.com</a>
                              <a className='singOut'>Sign out</a>
                          </div>
                      </div>
         </ul>

y modificamos los estilos y sevea wuai

@media (max-width: 640px) { .menu { display: block; position:relative; } .navbar-left ul { display: none; } .navbar-left .menu-left--active{ display: block; height: 100vh; width: 60%; position: absolute; margin-top: 60px; background-color: var(--white); padding-top: 20px; float: left; margin-left: -45%; animation: swipeLeft .40s; font-weight: bold; overflow: scroll; } .navbar-left ul li { padding: 20px 30px; color: var(--black); } .navbar-left ul li a { color: var(--black); } .line { border: 1px solid var(--very-light-pink); width: 80%; margin-left: 14% ; opacity: 25%; } .settings-user { width: 100%; height: 80px; display: flex; flex-direction: column; } .settings-user a { padding: 20px 40px; text-decoration: none; color: var(--black); } .user { display: flex; flex-direction: column; margin-top: 20px; } .user a { padding: 8px 40px; } .user .user-email { font-weight: 500; } .user .singOut { color: var(--hospital-green); } .navbar-email { display: none; } @keyframes swipeLeft { 0% { width: 0; } 100% { width: 60%; } } }

lvargascol commented 2 years ago

Agregue el componente MobileMenu.jsx y sus respectivos estilos MobileMenu.scss.

En vez de agregar un nuevo estado en Header.jsx para el Menu Mobile, utilice el mismo del Menu Desktop, pero alternando el display mediante Media Query.

Se agrega lo siguiente en Header.jsx:

<img src={menuIcon} alt="menu" className="menu-icon" onClick={handleToggle}/>

{toggle && <MobileMenu/>}

Se agrega lo siguiente en Menu.scss:

@media (max-width: 640px) { .menu { display: none; } }

Misael-GC commented 1 year ago

Comparto el componente y sus estilos MenuMobile.jsx ` import React from 'react'; import '../styles/MenuMobile.scss';

function MenuMobile() { return (

) }

export default MenuMobile; `

MenuMobile.scss ` @import 'vars';

.mobile-menu { background: var(--white); position: absolute; / add styles clase 19/ top: 45px; / add styles clase 19/ left: 0; width: 100%; padding: 12px 24px 24px 24px; / transition: 400ms; / display: none; z-index: 3; }

.mobile-menu a{ text-decoration: none; color: var(--black); font-weight: bold; }

.mobile-menu ul:not(.email-1){ padding: 0; margin: 24px 0 0; list-style: none; }

.mobile-menu ul:nth-child(1){ border-bottom: 1px solid var(--very-light-pink); }

.mobile-menu ul li{ margin-bottom: 22px; }

.mobile-menu .email{ font-size: var(--sm); font-weight: 300; }

.mobile-menu .sign-out{ font-size: var(--sm); color: var(--hospital-green); }

.email-1, #exp, #exp-1{ margin-bottom: 0; }

.email-1{ padding: 0; margin: 70px 0 0; list-style: none; }

@media (max-width: 640px) { .mobile-menu { display: inline-block; }

} `

Gustavolando commented 1 year ago

Reto superado:

Mi código:

MenuMobile.jsx:

import React from 'react'
import '@styles/MenuMobile.scss'
import close from '@icons/icon_close.png'

function MenuMobile( { onCerrar } ) {
  return (
    <div className="mobile-menu">
      <img src={close} alt="close menu" className="close" onClick={onCerrar} />
      <div>
        <ul>
          <li><a href="/">CATEGORIES</a></li>
          <li><a href="/">All</a></li>
          <li><a href="/">Cloth</a></li>
          <li><a href="/">Electronics</a></li>
          <li><a href="/">Furniture</a></li>
          <li><a href="/">Toys</a></li>
          <li><a href="/">Others</a></li>
        </ul>
        <ul>
          <li><a href="/">My orders</a></li>
          <li><a href="/">My Account</a></li>
        </ul>
      </div>
      <ul>
        <li><a href="/" className="email">camilayokoo@gmail.com</a></li>
        <li><a href="/" className="sign-out">Sign out</a></li>
      </ul>
    </div>
  )
}

export default MenuMobile

MenuMobile.scss:

.mobile-menu-container {
  position: fixed;
  left: 0;
}
.mobile-menu {
  position: fixed;
  top: 60px;
  bottom: 0;
  left: 0;
  height: calc(100% - 60px);
  box-sizing: border-box;
  width: 100%;
  padding: 0 24px;
  display: flex;
  flex-direction: column;
  justify-content: space-between;
  background: var(--white);
  opacity: 0.95;
}
.mobile-menu a {
  text-decoration: none;
  color: var(--black);
  font-weight: bold;
}
.mobile-menu div {
  margin: 0;
  padding: 0;
  width: 100%;
}
.mobile-menu ul {
  padding: 0;
  margin: 24px 0 0;
  list-style: none;
}
.mobile-menu ul:nth-child(1) {
  border-bottom: 1px solid var(--very-light-pink);
}
.mobile-menu ul:last-child {
  margin-bottom: 28px;
}
.mobile-menu div ul li {
  margin-bottom: 32px;
}
.mobile-menu .email {
  font-size: var(--sm);
  font-weight: 300;
  margin-bottom: 4px;
  display: inline-block;
}
.mobile-menu .sign-out {
  color: var(--hospital-green);
  font-size: var(--sm);
}
.mobile-menu .close {
  position: absolute;
  top: 8px;
  right: 0px;
  width: 16px;
  object-fit: cover;
  padding: 16px;
  border-radius: 50%;
  cursor: pointer;
}

Y los cambios en el Header.jsx:

import React, { useState, useContext, useRef } from 'react'
import '@styles/Header.scss'
import Menu from '@components/Menu'
import MyOrder from '@containers/MyOrder'
import MenuMobile from '@containers/MenuMobile'
import menu from '@icons/icon_menu.svg'
import logo from '@logos/logo_yard_sale.svg'
import AppContext from '@context/AppContext'
import shoppingCart from '@icons/icon_shopping_cart.svg'

const Header = () => {
  const [toggle, setToggle] = useState(false)
  const [toggleOrders, setToggleOrders] = useState(false)
  const [toggleMenuMobile, setToggleMenuMobile] = useState(false)
  const { state } = useContext(AppContext)

  const handleToggle = () => {
    setToggle(!toggle)
    // setToggleOrders(false)
  }

  const handleToggleOrders = () => {
    setToggleOrders(!toggleOrders)
    // setToggle(false)
  }

  const handleToggleMenuMobile = () => {
    setToggleMenuMobile(!toggleMenuMobile)
    // setToggleMenuMobile(false)
  }

  const userMenu = useRef(null)
  const menuMobile = useRef(null)
  const asideMyOrder = useRef(null)
  const closeOpenMenus = (e)=>{
    if (userMenu.current && toggle && !userMenu.current.contains(e.target)) {
      setToggle(false)
    }
    const navbarEmail = document.querySelector(".navbar-email")
    if (asideMyOrder.current && toggleOrders && !asideMyOrder.current.contains(e.target) && e.target != navbarEmail) {
      setToggleOrders(false)
    }
    if (menuMobile.current && toggleMenuMobile && !menuMobile.current.contains(e.target)) {
      setToggleMenuMobile(false)
    }
  }
  document.addEventListener('mousedown',closeOpenMenus)

  return (
    <nav>
      <img src={menu} alt="menu" className="menu" onClick={handleToggleMenuMobile} />
      <div className="navbar-left">
        <img src={logo} alt="logo" className="nav-logo" />
        <ul>
          <li>
            <a href="/">All</a>
          </li>
          <li>
            <a href="/">Cloth</a>
          </li>
          <li>
            <a href="/">Electronics</a>
          </li>
          <li>
            <a href="/">Furniture</a>
          </li>
          <li>
            <a href="/">Toys</a>
          </li>
          <li>
            <a href="/">Others</a>
          </li>
        </ul>
      </div>
      <div className="navbar-right">
        <ul>
          <li className="navbar-email" onClick={handleToggle}>
            user@example.com
          </li>
          <li 
            className="navbar-shopping-cart" 
            onClick={handleToggleOrders}
          >
            <img src={shoppingCart} alt="shopping cart" />
            {state.cart.length > 0 ? <div>{state.cart.length}</div> : null}
          </li>
        </ul>
      </div>
      <div className="menu-container" ref={userMenu}>
        {toggle && <Menu />}
      </div>
      <div className="my-order-container" ref={asideMyOrder}>
        {toggleOrders && <MyOrder onCerrar = {() => setToggleOrders(false)} />}
      </div>
      <div className="mobile-menu-container" ref={menuMobile}>
        {toggleMenuMobile && <MenuMobile onCerrar = {() => setToggleMenuMobile(false)} />}
      </div>
    </nav>
  )
}

export default Header

Para más detalles los invito a revisar el código: https://github.com/Gustavolando/react-shop

Nota: En el reto anterior expliqué porqué comenté los setToogle en los métodos handle