완료 기능 만들기

비얌·2023년 2월 19일
0
post-thumbnail

🧹 개요

투두리스트 완료 기능을 만들어보자! 일단 완료 버튼은 이거로 만들어보기로 했다.



✨ 결과 미리보기

만들려고 했던 기능에서 몇 개를 빼고 완성했다😂

나중에 지금 못만든 기능을 만드는 법을 알게 되면 추가하자.

  • 체크 아이콘 서서히 작아지게 하는 것
  • 배경색 밖에서부터 안쪽으로 채우는 것, 그리고 그 반대의 것

// TodoItem.js
<label>
  <input type="checkbox"/>
  <div>
    <FontAwesomeIcon icon={faCheck} color="#1a202c" className={styles.checkIcon} />
  </div>
</label>
/* TodoItem.module.css */
input[type="checkbox"] {
  display: none;
}

label div {
  display: flex;
  justify-content: center;
  align-items: center;
  width: 35px;
  height: 35px;
  background-color: #1a202c;
  border: 3px solid rgb(73, 220, 73);
  border-radius: 50%;
  cursor: pointer;
}

input[type="checkbox"]:checked +div {
  background-color: rgb(73, 220, 73);
}

.text {
  margin-left: 20px;
}

.checkIcon {
  width: 0%;
}

input[type="checkbox"]:checked +div .checkIcon{
  transition: width 0.2s ease;
  width: 100%;
}

🛫 과정

기본적인 체크박스 만들기

<input type="checkbox"></input>를 TodoItem 컴포넌트에 추가하였다. 그리고 체크박스와 텍스트를 왼쪽으로 몰기 위해 둘을 div로 묶었고, display: flex를 주어 가로로 정렬시켰다. (기본이 flex-direction: row이기 때문에 생략 가능하다고 함)

// TodoItem.js
return (
  <div className={styles.textAndDeteteBtn}>
    <div className={styles.checkboxAndText}>
      <input type="checkbox"></input>
      <div className={styles.text}>{props.todo.text}</div>
    </div>
    {isDeleteClicked ? 
      <div>
        <button className={styles.delteOkIcon} onClick={() => onDelete(props.todo.id)}><FontAwesomeIcon icon={faCheck} size="2x" color="white"/></button>
        <button className={styles.delteNoIcon} onClick={cancelDelete}><FontAwesomeIcon icon={faXmark} size="2x" color="white"/></button>
      </div>
      : <button className={styles.delteIcon} onClick={openDelete}><FontAwesomeIcon icon={faTrashCan} size="2x" color="white"/></button> 
    }
  </div>
  );
/* TodoItem.module.css */
.checkboxAndText {
  display: flex;
}

그랬더니 이렇게 텍스트가 중앙에 있지 않고 약간 위쪽에 정렬되는 것이다!

이유는, align-items 속성이 기본적으로 stretch로 설정되어 있기 때문인데, 이를 center로 설정해주면 해결된다.


label 만들기

1. input 옆에 label 쓰고 id로 연결하기 (실패)

checkbox 타입을 가진 input 아래에 for로 input과 결합한 label을 만들었다. label은 폼의 양식에 이름을 붙이는 태그이다.

label을 클릭하면 결합된 요소도 클릭이 되기 때문에 이를 이용하여 체크박스를 디자인 할 것이다.

html은 이렇게 만들었다. 이제 기존에 체크박스가 있던 input 요소는 display: none으로 안보이게 만들고, label에서 새로운 체크박스 모양을 만들고 디자인하여 보여줄 것이다.

// TodoItem.js
<input type="checkbox" id="checkboxId" />
<label for="checkboxId"></label>
/* TodoItem.module.css */
input[type="checkbox"] {
  display: none;
}

label {
  display: inline-block;
  width: 35px;
  height: 35px;
  background-color: #1a202c;
  border: 3px solid rgb(73, 220, 73);
  border-radius: 50%;
  cursor: pointer;
}

input[type="checkbox"]:checked + label {
  background-color: rgb(73, 220, 73);
}
  • type이 checkbox인 input의 display를 none으로 설정하여 기존의 네모난 체크박스를 숨겼다.

  • label을 디자인했다.

  • type이 checkbox인 input이 체크되었을 때 input에 인접한 label의 색을 바꾼다.
    💥 여기서 +가 왜 들어가는지 몰라서 지워봤는데, 속성이 적용되지 않았다. 왜냐하면 input + label은 input에 인접한 label이라는 뜻인데 반해 input label은 input 안에 있는 label이라는 뜻이기 때문이다. 코드를 보면 알 수 있지만 label은 input에 인접하여있지 안에 위치하고 있지 않다.

    <input type="checkbox" id="checkboxId" />
    <label for="checkboxId"></label>

하지만 이렇게 하면 다른 투두의 체크박스를 클릭해도 오직 맨 위에 있는 체크박스만 변경이 된다. 이 이유는 적용되는 id가 같기 때문이다. 따라서 다른 방법을 써보자.

2. input 상위에 label 쓰기 (성공)

label 안에 input을 쓰고, 그 안에 있는 div로 버튼을 만들고 디자인 할 것이다.

// TodoItem.js
<label>
  <input type="checkbox" />
  <div />
</label>
/* TodoItem.module.css */
input[type="checkbox"] {
  display: none;
}

label div {
  display: inline-block;
  width: 35px;
  height: 35px;
  background-color: #1a202c;
  border: 3px solid rgb(73, 220, 73);
  border-radius: 50%;
  cursor: pointer;
}

label input[type="checkbox"]:checked + div {
  background-color: rgb(73, 220, 73);
}
  • label 안에 있는 div로 버튼을 디자인했다.

  • label 안에 있는 input이 체크되면 input과 인접한 div의 배경색을 바꿨다.

3. :has() 사용하기 (성공)

:has()는 최근에 추가된 문법이라고 한다. input이 체크되면 label의 속성을 변경할 수 있다.

// TodoItem.js
<label>
  <input type="checkbox" />
</label>
/* TodoItem.module.css */
input[type="checkbox"] {
  display: none;
}

label  {
  display: inline-block;
  width: 35px;
  height: 35px;
  background-color: #1a202c;
  border: 3px solid rgb(73, 220, 73);
  border-radius: 50%;
  cursor: pointer;
}

label:has(input[type="checkbox"]:checked) {
  background-color: rgb(73, 220, 73);
}

중간 결과


체크 표시 만들기

체크 표시 아이콘 삽입하기

먼저 체크 박스를 누르면 나타나는 체크 아이콘을 삽입해보자. FontAwesome에서 아이콘을 가져왔다. 리액트에서 FontAwesome을 사용하는 방법은 지난번 포스팅에 정리해놨다.

<label>
  <input type="checkbox" />
  <div>
    <FontAwesomeIcon icon={faCheck} color="white" />
  </div>
</label>

label div에 flexbox를 설정해서 아래의 코드를 추가해주었다.

display: flex;
justify-content: center;
align-items: center;

체크 표시 아이콘 나타났다 사라졌다 하게 하기

isChecked라는 state를 만들어 체크됨과 체크되지 않는 상태를 기록하였고, 이 상태에 따라 체크 표시를 화면에 표시하거나 표시하지 않았다.

// TodoItem.js
const [isChecked, setIsChecked] = useState(false);

<label>
  <input type="checkbox" onClick={() => setIsChecked(!isChecked)} />
  <div>
    {isChecked && <FontAwesomeIcon icon={faCheck} color="white" />}
  </div>
</label>

중간 결과

체크 표시 아이콘 서서히 나타나게 하기

하지만 체크 버튼을 누르면 서서히 나타나게 변경하고 싶었다. 그래서 체크버튼이 눌리기 전에는 크기가 0%로 보이지 않다가, 누르면 서서히 커지게 했다. (state인 isChecked를 없애고 input[type="checkbox"]:checked를 사용했다)

/* TodoItem.module.css */ 
.checkIcon {
  width: 0%;
}

input[type="checkbox"]:checked +div .checkIcon{
  transition: width 0.2s ease;
  width: 100%;
}

중간 결과

0.2초간 서서히 확대했다!


체크하면 글자 흐리게 하기

체크한 투두의 글자 색을 회색으로 바꾸어 완료된 투두라는 것을 더 확실히 하고 싶었다. 그래서 state로 isChecked를 추가하여 만들었다.(이걸 추가하지 않고 css로만 하는 방법을 모르겠다)

기존의 코드에 아래의 코드를 추가했다.

const [isChecked, setIsChecked] = useState(false);

<div className={isChecked? `${styles.text} ${styles.checked}` : `${styles.text}`}>{props.todo.text}</div>
.text.checked {
  color: rgb(107, 107, 107);
}


✨ 결과

완성했다!



🐹 회고

체크박스를 label로 커스텀 하는 법을 알게 되어 유익했다!
(처음에는 이미지로 체크박스를 만들려고 했으나 그렇게 되면 tab 버튼으로 체크 박스를 선택할 수 없고 시각장애인이 이것이 체크박스인 것을 알 수 없는 등의 문제가 있다고 하여 커스텀하게 됨)

그리고 더 세련되게 만들고 싶었지만 여기서 마무리해서 아쉽다 👀

완료 기능을 만들기 위해 구글에서 예시를 찾아봤는데, Beautiful CSS checkboxes examples 이곳에서 많은 데모와 코드를 볼 수 있어서 유용했다.

다음 포스팅에서는 수정 기능을 만들어보자!

profile
🐹강화하고 싶은 기억을 기록하고 공유하자🐹

0개의 댓글