02/22

noway·2023년 2월 22일
0

개똥벌레

목록 보기
7/7

Modal

import { useState } from 'react';
import styled from 'styled-components';

export const ModalContainer = styled.div`
  // TODO : Modal을 구현하는데 전체적으로 필요한 CSS를 구현합니다.
  display: flex;
  justify-content: center;
  align-items: center;
  height: 100%;
`;

export const ModalBackdrop = styled.div`
  // TODO : Modal이 떴을 때의 배경을 깔아주는 CSS를 구현합니다.
  background-color:rgba(0, 0, 0, 0.3); // rgba=rgb에 alpha channel을 더한 값으로 0~1 사이로 투명도 조절 가능
  display: flex;
  justify-content: center;
  align-items: center;
  width: 100%;
  height: 100%;  
  position: fixed; // fixed로 설정할 경우 원하는 위치에 자유롭게 설정 가능
  top: 0;
  left: 0;
  right: 0;
  bottom: 0; // 상하좌우 여백없이 채우기

`;

export const ModalBtn = styled.button`

  background-color: var(--coz-purple-600);
  text-decoration: none;
  border: none;
  padding: 20px;
  color: white;
  border-radius: 30px;
  cursor: grab; // 버튼에 다가가면 나오는 마우스 설정, 다른 값들은 [](https://developer.mozilla.org/ko/docs/Web/CSS/cursor)참조
`;

export const CloseBtn = styled.button`
color:white;
  display: flex;
  justify-content: center;
  align-items: center;
background-color:rgb(0, 0, 0);
width: 40px;
height: 40px;  
cursor: pointer;
`

export const ModalView = styled.div.attrs((props) => ({
  // attrs 메소드를 이용해서 아래와 같이 div 엘리먼트에 속성을 추가할 수 있습니다.
  role: 'dialog',
}))`
  // TODO : Modal창 CSS를 구현합니다.
  display: flex;
  justify-content: center;
  align-items: center;
  width: 200px;
  height: 100px;
  background-color: white;
  border: none;
  padding: 35px;
  color: var(--coz-purple-600);
  border-radius: 5px;
`;

export const Modal = () => {
  const [isOpen, setIsOpen] = useState(false);

  const openModalHandler = () => {
    // TODO : isOpen의 상태를 변경하는 메소드를 구현합니다.
    setIsOpen(!(isOpen)) // 실행시 isOpen을 반대값으로 만듬 - true -> false, false -> true
  };

  return (
    <>
      <ModalContainer>
        <ModalBtn
        // TODO : 클릭하면 Modal이 열린 상태(isOpen)를 boolean 타입으로 변경하는 메소드가 실행되어야 합니다.
        onClick={openModalHandler} // ModalBtn을 클릭하면 openModalHandler 실행
        >
          {/* TODO : 조건부 렌더링을 활용해서 Modal이 열린 상태(isOpen이 true인 상태)일 때는 ModalBtn의 내부 텍스트가 'Opened!' 로 Modal이 닫힌 상태(isOpen이 false인 상태)일 때는 ModalBtn 의 내부 텍스트가 'Open Modal'이 되도록 구현해야 합니다. */}
          {isOpen ? 'Opened!' : 'Open Modal'} // isOpen이 true일 경우 Opened!, false일 경우 Open Modal
        </ModalBtn>
        {/* TODO : 조건부 렌더링을 활용해서 Modal이 열린 상태(isOpen이 true인 상태)일 때만 모달창과 배경이 뜰 수 있게 구현해야 합니다. */}
        {isOpen ? <ModalBackdrop onClick={openModalHandler}> // ModalBackdrop 클릭하면 openModalHandler 실행
          <ModalView onClick={function(e){e.stopPropagation()}}> HELLO CODESTATES! // e.stopPropagation()= 부모에게서 오는 이벤트를 막음 = ModalBackdrop에서 onClick={openModalHandler} 이벤트가 내려왔으나 ModalView는 해당 이벤트가 실행되지않음
            <CloseBtn onClick={openModalHandler}> // CloseBtn 클릭하면 openModalHandler 실행, 원래라면 ModalBackdrop에서 onClick={openModalHandler} 이벤트를 받았으나 ModalView에서 e.stopPropagation() 실행으로 CloseBtn도 e.stopPropagation()를 물려받은 상태로 onClick={openModalHandler} 이벤트 실행을 원할경우 다시 기재해야됨
              x
            </CloseBtn>
          </ModalView>
          </ModalBackdrop> : null} // isOpen이 true일 경우 ModalBackdrop 실행, false일 경우 아무것도 실행안함(null)
      </ModalContainer>
    </>
  );
};

toggle

import { useState } from 'react';
import styled from 'styled-components';

const ToggleContainer = styled.div`
  position: relative;
  margin-top: 8rem;
  left: 47%;
  cursor: pointer;

  > .toggle-container {
    width: 50px;
    height: 24px;
    border-radius: 30px;
    background-color: #8b8b8b;
    // TODO : .toggle--checked 클래스가 활성화 되었을 경우의 CSS를 구현합니다.
    &.toggle--checked{
      background-color: blue;
    }
  }

  > .toggle-circle {
    position: absolute;
    top: 1px;
    left: 1px;
    width: 22px;
    height: 22px;
    border-radius: 50%;
    background-color: #ffffff;
    // TODO : .toggle--checked 클래스가 활성화 되었을 경우의 CSS를 구현합니다.
    &.toggle--checked{
    position: absolute;
    top: 1px;
    left: 27px;
    width: 22px;
    height: 22px;
    }
  }
`;

const Desc = styled.div`
  // TODO : 설명 부분의 CSS를 구현합니다.
  display: flex;
  justify-content: center;
  align-items: center;
`;

export const Toggle = () => {
  const [isOn, setisOn] = useState(false);

  const toggleHandler = () => {
    // TODO : isOn의 상태를 변경하는 메소드를 구현합니다.
    setisOn(!isOn) // 실행시 isOpen을 반대값으로 만듬 - true -> false, false -> true
  };
  };

  return (
    <>
      <ToggleContainer
        // TODO : 클릭하면 토글이 켜진 상태(isOn)를 boolean 타입으로 변경하는 메소드가 실행되어야 합니다.
        onClick={toggleHandler} // ToggleContainer를 클릭하면 toggleHandler 실행
      >
        {/* TODO : 아래에 div 엘리먼트 2개가 있습니다. 각각의 클래스를 'toggle-container', 'toggle-circle' 로 지정하세요. */}
        {/* TIP : Toggle Switch가 ON인 상태일 경우에만 toggle--checked 클래스를 div 엘리먼트 2개에 모두 추가합니다. 조건부 스타일링을 활용하세요. */}
        <div className={`toggle-container ${isOn ? "toggle--checked" : ""}`} />
        <div className={`toggle-circle ${isOn ? "toggle--checked" : ""}`} /> // isOn상태가 true면 div에다가 클래스명 toggle--checked를 추가한다, false면 추가X 
      </ToggleContainer>
      {/* TODO : Desc 컴포넌트를 활용해야 합니다. */}
      <Desc>
      {/* TIP:  Toggle Switch가 ON인 상태일 경우에 Desc 컴포넌트 내부의 텍스트를 'Toggle Switch ON'으로, 그렇지 않은 경우 'Toggle Switch OFF'가 됩니다. 조건부 렌더링을 활용하세요. */}
      {isOn ? 'Toggle Switch ON' : 'Toggle Switich OFF'} // isOn 상태가 true면 text가 Toggle Switch ON, false면 Toggle Switich OFF
      </Desc>
    </>
  );
};

tap

import { useState } from 'react';
import styled from 'styled-components';

// TODO: Styled-Component 라이브러리를 활용해 TabMenu 와 Desc 컴포넌트의 CSS를 구현합니다.

const TabMenu = styled.ul`
  background-color: #dcdcdc;
  color: rgba(73, 73, 73, 0.5);
  font-weight: bold;
  display: flex;
  flex-direction: row;
  justify-items: center;
  align-items: center;
  list-style: none;
  margin-bottom: 7rem;

  .submenu {
    ${'' /* 기본 Tabmenu 에 대한 CSS를 구현합니다. */}
    display: flex;
    padding: 10px;
    font-size: 20px;
    transition: 1s;
    cursor: pointer;
    flex-grow : 1;
  }

  .focused {
    ${'' /* 선택된 Tabmenu 에만 적용되는 CSS를 구현합니다.  */}
    background-color: magenta;
  }

  & div.desc {
    text-align: center;
  }
`;

const Desc = styled.div`
  text-align: center;
`;

export const Tab = () => {
  // TIP: Tab Menu 중 현재 어떤 Tab이 선택되어 있는지 확인하기 위한
  // currentTab 상태와 currentTab을 갱신하는 함수가 존재해야 하고, 초기값은 0 입니다.
  const [currentTab, setCurrentTab] = useState(0) 

  const menuArr = [
    { name: 'Tab1', content: 'Tab menu ONE' },
    { name: 'Tab2', content: 'Tab menu TWO' },
    { name: 'Tab3', content: 'Tab menu THREE' },
  ];

  const selectMenuHandler = (index) => {
    // TIP: parameter로 현재 선택한 인덱스 값을 전달해야 하며, 이벤트 객체(event)는 쓰지 않습니다
    // TODO : 해당 함수가 실행되면 현재 선택된 Tab Menu 가 갱신되도록 함수를 완성하세요.
    
    setCurrentTab(index); // 변수로 들어오는 index값을 currentTab애 할당
  };

  return (
    <>
      <div>
        <TabMenu>
          {/*TIP: li 엘리먼트의 class명의 경우 선택된 tab 은 'submenu focused' 가 되며, 
                  나머지 2개의 tab은 'submenu' 가 됩니다.*/}
                  {menuArr.map((ele, index) => (
                    <li onClick={() => selectMenuHandler(index) // li를 클릭하면 selectMenuHandler(index)에 현재 누른 값의 index 전달 ex) 'Tab1' 클릭할시 Tab1은 MenuArr의 0번째 인덱스 > selectMenuHandler(0)
                    className ={index === currentTab ? 'submenu focused' : 'submenu'}>{ele.name} </li> // li의 클래스명은 index와 currentTab이 같은 경우 submenu focused 다른 경우 submenu로 할당, li의 text는 ele의 name값 ex) Tab1 선택시 Tab1의 class는 submenu focused, Tab2와 Tab3의 class는 submenu로, li의 text는 Tab1의 name값인 Tab1
                  ))} 
        </TabMenu>
        <Desc>
          <p>{menuArr[currentTab].content}</p>
        </Desc>
      </div>
    </>
  );
};

Tag

import { useState } from 'react';
import styled from 'styled-components';

// TODO: Styled-Component 라이브러리를 활용해 여러분만의 tag 를 자유롭게 꾸며 보세요!

export const TagsInput = styled.div`
  margin: 8rem auto;
  display: flex;
  align-items: flex-start;
  flex-wrap: wrap;
  min-height: 48px;
  width: 480px;
  padding: 0 8px;
  border: 1px solid rgb(214, 216, 218);
  border-radius: 6px;

  > ul {
    display: flex;
    flex-wrap: wrap;
    padding: 0;
    margin: 8px 0 0 0;

    > .tag {
      width: auto;
      height: 32px;
      display: flex;
      align-items: center;
      justify-content: center;
      color: #fff;
      padding: 0 8px;
      font-size: 14px;
      list-style: none;
      border-radius: 6px;
      margin: 0 8px 8px 0;
      background: var(--coz-purple-600);
      > .tag-close-icon {
        display: block;
        width: 16px;
        height: 16px;
        line-height: 16px;
        text-align: center;
        font-size: 14px;
        margin-left: 8px;
        color: var(--coz-purple-600);
        border-radius: 50%;
        background: #fff;
        cursor: pointer;
      }
    }
  }

  > input {
    flex: 1;
    border: none;
    height: 46px;
    font-size: 14px;
    padding: 4px 0 0 0;
    :focus {
      outline: transparent;
    }
  }

  &:focus-within {
    border: 1px solid var(--coz-purple-600);
  }
`;

export const Tag = () => {
  const initialTags = ['CodeStates', 'kimcoding'];

  const [tags, setTags] = useState(initialTags);
  const removeTags = (indexToRemove) => {
    // TODO : 태그를 삭제하는 메소드를 완성하세요.
    setTags(tags.filter((_, index) => index !== indexToRemove)); //	indexToRemove로 들어온 index값과 같지 않은 tags의 index값만 리턴 = 같은 index는 리턴X
  };

  const addTags = (event) => {
    // TODO : tags 배열에 새로운 태그를 추가하는 메소드를 완성하세요.
    // 이 메소드는 태그 추가 외에도 아래 3 가지 기능을 수행할 수 있어야 합니다.
    // - 이미 입력되어 있는 태그인지 검사하여 이미 있는 태그라면 추가하지 말기
    // - 아무것도 입력하지 않은 채 Enter 키 입력시 메소드 실행하지 말기
    // - 태그가 추가되면 input 창 비우기
    const filtered = tags.filter((el) => el === event.target.value); // event.target.value는 text값 // tags와 event.target.value 같은 값만 리턴 = 이미 추가된 태그값이면 리턴값이 있음
    if (event.target.value !== '' && filtered.length === 0) { // event.target.value가 빈 문자가 아니고  filtered.length가 0일 때(겹치는 값이 아닐때)
      setTags([...tags, event.target.value]); // tags에다가 event.target.value 추가
      event.target.value = ''; // text창 초기화
    }
  };

  return (
    <>
      <TagsInput>
        <ul id="tags">
          {tags.map((tag, index) => (
            <li key={index} className="tag">
              <span className="tag-title">{tag}</span>
              <span className="tag-close-icon" onClick={() => removeTags(index)}>X // span 클릭하면 removeTags(index) 실행 
                {/* TODO :  tag-close-icon이 tag-title 오른쪽에 x 로 표시되도록 하고,
                            삭제 아이콘을 click 했을 때 removeTags 메소드가 실행되어야 합니다. */}
                            
              </span>
            </li>
          ))}
        </ul>
        <input
          className="tag-input"
          type="text"
          onKeyUp={(event) => (event.key === 'Enter' ? addTags(event) : null) // 엔터키를 누르면 addTags(event) 실행, 아니면 아무것도 실행하지 않음
          }
          placeholder="Press enter to add tags"
        />
      </TagsInput>
    </>
  );
};

0개의 댓글

관련 채용 정보