React로 toggle,tab,tag 만들어보기

돌리의 하루·2023년 2월 22일
11
post-thumbnail

toggle 구현코드

알고 가야할 점!🧐

  • state로 무슨 상태를 설정해야할까?
    버튼이 눌렸을때 ( on ) / 버튼이 눌리지 않았을때 ( off )

  • 버튼이 on 상태일 때 추가할 css를 toggle-checked class를 만들고 조건으로 붙여준다.

  • toggle은 class를 조건에 따라 붙여주는 것이 핵심이다!
 <div className={`toggle-container ${isOn ? "toggle--checked" : null}`}/>
 <div className={`toggle-circle ${isOn ? "toggle--checked" : null}`} />

위의 코드를 살펴보고 class의 중복사용에 대해 알고가자!


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

  const toggleHandler = () => {
    setisOn(!isOn);
  };

  return (
    <>
      <ToggleContainer
        onClick={toggleHandler}
      >
        <div
          className={`toggle-container ${isOn ? "toggle--checked" : null}`}
        />
        <div className={`toggle-circle ${isOn ? "toggle--checked" : null}`} />
      </ToggleContainer>
      {isOn === false ? (
        <Desc>
          <div className="switch--Off"> 'Toggle Switch OFF'</div>
        </Desc>
      ) : (
        <Desc>
          <div className="switch--On">'Toggle Switch ON'</div>
        </Desc>
      )}
    </>
  );
};

toggle버튼 CSS

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;
    
  }
  > .toggle--checked {
    background-color: pink;
  }

  > .toggle-circle {
    position: absolute;
    top: 1px;
    left: 1px;
    width: 22px;
    height: 22px;
    border-radius: 50%;
    background-color: #ffffff;
    transition: 0.2s;
   
  }
  > .toggle--checked {
    left: 28px;
    transition: 0.4s;
  }
`;

const Desc = styled.div`
  display: flex;
  align-items: center;
  justify-content: center;

  > .switch--On {
    color: #64cd3c;
  }

  > .switch--Off {
    color: #853c3c;
  }
`;

tab 구현코드

알고 가야할 점!🧐

  • Tab을 관리할때 index로 관리해보자🐹

  • 인덱스를 받아 탭의상태를 변경하는 함수를 만든다.
const selectMenuHandler = (index) => {
    setTab(index);
  };

* map으로 탭을 관리하는 배열을 가져온 후 tab, index를 인자로 받는다. className은 현재 Tab이 index와 같을 때와 다를 때 각각 설정해주고, 이벤트로 탭의상태를 변경해주는 함수를 넣는다👍

export const <Tab = () => {
  const [Tab, setTab] = 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) => {
    setTab(index);
  };

  return (
    <>
      <div>
        <TabMenu>
          {menuArr.map((tab, index) => {
            return (
              <li
                key={index}
                className={Tab === index ? "submenu focused" : "submenu"}
                onClick={() => selectMenuHandler(index)}
              >
                {tab.name}
              </li>
            );
          })}
        </TabMenu>
        <Desc>
          <p>{menuArr[Tab].content}</p>
        </Desc>
      </div>
    </>
  );
};

tab버튼 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;
  cursor: pointer;

  .submenu {
    display: flex;
    justify-content: center;
    align-items: center;
    width: 300px;
    height: 50px;
    padding: 20px;
  }

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

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

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

tag 구현코드

알고 가야할 점!🧐

  • removeTag를 관리할때 index로 관리해보자🐹

  • tags가 삭제,추가 되니까 관리하는 state를 만들자!

  • 태그를 추가할때의 input의 조건은 "enter"가 들어올 때, 인풋에 작성한 내용이 태그에 없을 때, 인풋이 비어있지 않을때다!
    조건을 잘 살펴보자🐝

  • 위의 tab에서 인덱스로 상태를 관리한 것처럼, removeTag에 그대로 활용해볼것이다!
export const Tag = () => {
  const initialTags = ["CodeStates", "kimcoding"];

  const [tags, setTags] = useState(initialTags);
  
  const removeTags = (indexToRemove) => {
   setTags(
      tags.filter((tag) => {
        return tag !== tags[indexToRemove];
      })
    );
  };

  const addTags = (event) => {
    //인풋value(내가 새로 치는 부분)을 변수로 설정하고 조건을 정리해보면 변수는 Mango
    //event.key === "Enter"
    //Mango가 비어있지 않을때
    //태그의 내용이 Mango일때 > tags.includes(Mango)

    let input = event.target.value;

    if (event.key === "Enter" && !tags.includes(input) && input !== "") {
      setTags([...tags, input]);
      event.target.value = ""; //여기 input ="";라고 썼을때 안됐음. 변수가 event.target.value의 고유기능까지 대신 못하나봄..?
    }
  };

  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>
            </li>
          ))}
        </ul>
        <input
          className="tag-input"
          type="text"
          onKeyUp={(event) => {
            addTags(event);
          }}
          placeholder="Press enter to add tags"
        />
      </TagsInput>
    </>
  );
};

tag - css

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);
  }
`;
profile
진화중인 돌리입니다 :>

0개의 댓글