Menu 컴포넌트

q6hillz·2022년 4월 19일
0

import React, { useEffect, useRef } from 'react';
import { useMediaQuery } from 'react-responsive';
import './Menu.css';
import { Link } from 'react-router-dom';
import styled from "styled-components";

const StyledLink = styled(Link)`
  text-decoration: none;
  color: white;
`

function Menu() {
  let isMb = useMediaQuery({
    query: "(max-width:414px)"
  })
  let ManipulatedObj = {
    listObj: useRef(),
    button: useRef(),
  }
  let sw = 0;
  const fadein = fadeIn.bind(ManipulatedObj);
  const fadeout = fadeOut.bind(ManipulatedObj);

  function fadeIn() {
    this.listObj.current.style.visibility = 'visible';
    for (let i = 0; i < this.listObj.current.children.length; i++) {
      this.listObj.current.children[i].style.opacity = 1;
    }
  }

  function fadeOut() {
    this.listObj.current.style.visibility = 'hidden';
    for (let i = 0; i < this.listObj.current.children.length; i++) {
        this.listObj.current.children[i].style.opacity = 0;
    }
  }

  function ButtonPointerEnter() {
    fadein();
  }

  function ButtonMouseLeave(event) {
    if (event.relatedTarget === window || event.relatedTarget === null) {
      fadeout();
    }
    else if (!event.relatedTarget.closest('.list')) {
      fadeout();
    }
  }

  function ListMouseLeave(event) {
    if (event.relatedTarget === window || event.relatedTarget === null) {
      fadeout();
    }
    else if (event.relatedTarget === this.button.current) {
    }
    else if (!event.relatedTarget.closest('.list')) {
      fadeout();
    }
  }

  function ButtonOnClick() {
    if (!sw) {
      this.listObj.current.style.visibility = 'visible';
      fadein();
      sw = !sw;
    }
    else {
      this.listObj.current.style.visibility = 'hidden';
      fadeout();
      sw = !sw;
    }
  }

  useEffect(() => {
    if (!isMb) {
      ManipulatedObj.button.current.onpointerenter = ButtonPointerEnter.bind(ManipulatedObj);
      ManipulatedObj.button.current.onmouseleave = ButtonMouseLeave.bind(ManipulatedObj);
      ManipulatedObj.listObj.current.onmouseleave = ListMouseLeave.bind(ManipulatedObj);
      ManipulatedObj.button.current.onclick = undefined;
    }
    else {
      ManipulatedObj.button.current.onpointerenter = undefined;
      ManipulatedObj.button.current.onmouseleave = undefined;
      ManipulatedObj.listObj.current.onmouseleave = undefined;
      ManipulatedObj.button.current.onclick = ButtonOnClick.bind(ManipulatedObj);
    }
  }, [isMb]);

  return (
    <div className="container">
      <button className="button" ref={ManipulatedObj.button}>MENU</button>
      <ul className="list" ref={ManipulatedObj.listObj}>
        <li><StyledLink to='/'>HOME</StyledLink></li>
        <li><StyledLink to='/music'>MUSIC</StyledLink></li>
        <li><StyledLink to='/contact'>CONTACT</StyledLink></li>
      </ul>
    </div>
  );
}

export default Menu;

styled components를 사용하여 styled 메소드를 통해 Link 컴포넌트를
Decorate한다. 중요한 점은 useMediaQuery hook을 사용하고 화면의 크기를 추종하여 useEffect hook의 컴포넌트 생명 주기 업데이트를 실현하게 된다. 갱신될때 마다 PC에서 설정되어야하는 이벤트와 Mobile에서 설정되어야 하는 이벤트가 각각 다르므로 초기화 및 재설정해줘야 하는 부분이 존재한다.


css

.container {
    display: flex;
    flex-direction: column;
    align-items: flex-end;
    margin: 0;
    padding: 0;
}

.list {
    padding: 0;
    margin: 0;
    font-size: 3rem;
    list-style: none;
    display: table-cell;
    visibility: hidden;
    cursor: pointer;
    z-index: 1;
    -webkit-tap-highlight-color: black;  
}

.button {
    font-size: 4rem;
    padding: 0;
    margin: 0;
    background-color: black;
    color: white;
    border: none;
    cursor: pointer;
    z-index: 1;
}

.list li:hover {
    background-color: gray;
}

.list li:nth-child(1){
    opacity: 0;
    transition: opacity 0.2s;
}
.list li:nth-child(2){
    opacity: 0;
    transition: opacity 0.6s;
}
.list li:nth-child(3){
    opacity: 0;
    transition: opacity 1s;
}

.button {
    -webkit-tap-highlight-color: black;    
}

@media screen and (max-width: 414px) {
    .button {
        font-size: 3rem;
        padding: 0;
        margin: 0;
        background-color: black;
        color: white;
        border: none;
        cursor: pointer;
    }

    .list {
        font-size: 1.8rem;
        -webkit-tap-highlight-color: transparent;
    }

    .list li:hover {
        background-color: black;
    }

    .button {
        -webkit-tap-highlight-color: transparent;    
    }
}

버튼 전체 부분을 감싼 부모 컨테이너의 display는 flex, direction은 column, align-items 부분은 direction의 반대 부분이 되므로 flex-end로 상단 오른쪽 끝 부분에 위치 할 수 있도록 한다.

list와 같은 경우 마우스 hover가 적용되어야 하는 부분은 빈 공간 없이 타이트하게 글자를 감싸야하므로 display: table-cell 구조가 채택된다.

:nth-child(n)와 같은 경우는 셀렉터에 해당되는 그룹에서 n순번째를 뽑아 적용할 수 있는 의사 클래스이다.

0개의 댓글