앞서 만든 TodoList를 styled-components를 활용하여 꾸며보자.
테마 설정하기
컬러 팔레트를 하나 선택하여(https://colorhunt.co/) 해당 색상으로 palatte에 등록한다. (theme provider 사용)
/styles/theme.js
const palette = {
yellow: '#F9F9C5',
green: '#D9F8C4',
orange: '#FAD9A1',
red: '#F37878',
}
const common = {
flexCenter: `
display: flex;
align-items: center;
justify-content: center;
`,
flexAround: `
display: flex;
align-items: center;
justify-content: space-around;
`,
flexColumnStart: `
display: flex;
flex-direction: column;
align-items: center;
justify-content: flex-start;
`,
}
const fontSizes = {
title: '2rem',
subtitle: '1.5rem',
paragraph: '1rem',
}
const theme = {
palette,
common,
fontSizes,
}
export default theme
스타일 작성하기
/TodoList/style.js
import styled from 'styled-components'
export const TodoContainer = styled.div`
${({ theme }) => theme.common.flexColumnStart};
background-color: ${({ theme }) => theme.palette.red};
border-radius: 10px;
color: white;
width: 50rem;
height: 33rem;
margin: 3rem auto;
font-family: 'NotoSansBold';
`
export const TodoTitle = styled.p`
font-size: ${({ theme }) => theme.fontSizes.subtitle};
`
/TodoList/index.jsx
import React, { useState, useRef } from 'react'
import TodoAdd from "@/components/TodoAdd"
import TodoItem from "@/components/TodoItem"
import * as S from './style'
function TodoList() {
const todoId = useRef(2)
const [todoData, setTodoData] = useState([
{ id: 1, date: '2022-07-28', content: '강의하기', checked: false },
])
const todoRemoveHandler = (id) => {
setTodoData(todoData.filter((itemData) => itemData.id !== id))
}
const todoCheckHandler = (id) => {
setTodoData(
todoData.map((itemData) =>
itemData.id === id
? { ...itemData, checked: !itemData.checked }
: itemData,
),
)
}
return (
<S.TodoContainer>
<S.TodoTitle>나만의 Todo List</S.TodoTitle>
<TodoAdd todoId={todoId} todoData={todoData} setTodoData={setTodoData} />
{todoData.map((itemData) => {
return (
<TodoItem
key={itemData.id}
itemData={itemData}
todoCheckHandler={todoCheckHandler}
todoRemoveHandler={todoRemoveHandler}
/>
)
})}
</S.TodoContainer>
)
}
export default TodoList
/TodoAdd/style.js
import styled from 'styled-components'
export const AddContainer = styled.div`
${({ theme }) => theme.common.flexAround};
flex-shrink: 0;
color: black;
width: 90%;
margin-bottom: 1rem;
font-family: 'NotoSansBold';
`
export const AddButton = styled.button`
${({ theme }) => theme.common.flexCenter};
border: none;
outline: none;
cursor: pointer;
background-color: ${({ theme }) => theme.palette.green};
height: 2rem;
width: 5rem;
border-radius: 5px;
&:hover {
opacity: 0.5;
}
`
export const AddInput = styled.input`
border: none;
outline: none;
border-radius: 5px;
padding: 0rem 0.5rem;
height: 2rem;
width: 30%;
`
/TodoAdd/index.jsx
import React, { useState } from 'react'
import * as S from './style'
function TodoAdd({ todoId, todoData, setTodoData }) {
const [userInput, setUserInput] = useState({ date: '', content: '' })
const userInputHandler = (e) => {
const { name, value } = e.target
setUserInput({ ...userInput, [name]: value })
}
const todoAddHandler = (userInput) => {
setTodoData([
...todoData,
{
id: todoId.current,
date: userInput.date,
content: userInput.content,
checked: false,
},
])
todoId.current += 1
}
return (
<S.AddContainer>
<S.AddInput type="date" name="date" onChange={userInputHandler} />
<S.AddInput name="content" onChange={userInputHandler} />
<S.AddButton onClick={() => todoAddHandler(userInput)}>
추가하기
</S.AddButton>
</S.AddContainer>
)
}
export default TodoAdd
/TodoItem/style.js
import styled from 'styled-components'
export const ItemContainer = styled.div`
${({ theme }) => theme.common.flexAround};
flex-shrink: 0;
background-color: ${({ theme }) => theme.palette.yellow};
border-radius: 10px;
color: black;
width: 90%;
height: 5rem;
margin: 0.5rem;
font-family: 'NotoSansBold';
`
export const ItemButton = styled.div`
${({ theme }) => theme.common.flexCenter};
border: none;
outline: none;
cursor: pointer;
&:hover {
opacity: 0.5;
}
`
export const ItemText = styled.div`
font-size: ${({ theme }) => theme.fontSizes.paragraph};
width: 30%;
`
체크박스와 제거하기 버튼을 아이콘을 사용하도록 하기 위해 react-icons를 설치한다.
(참고: https://react-icons.github.io/react-icons/search?q=checkbox)
yarn add react-icons
/TodoItem/index.jsx
import React from 'react'
import * as S from './style'
import { GrCheckbox, GrCheckboxSelected } from 'react-icons/gr'
import { AiOutlineCloseCircle } from 'react-icons/ai'
function TodoItem({ itemData, todoCheckHandler, todoRemoveHandler }) {
const { id, date, content, checked } = itemData
return (
<S.ItemContainer>
<S.ItemButton onClick={() => todoCheckHandler(id)}>
{checked ? <GrCheckboxSelected /> : <GrCheckbox />}
</S.ItemButton>
<S.ItemText>{date}</S.ItemText>
<S.ItemText>{content}</S.ItemText>
<S.ItemButton onClick={() => todoRemoveHandler(id)}>
<AiOutlineCloseCircle />
</S.ItemButton>
</S.ItemContainer>
)
}
export default TodoItem
완료 여부 수정
완료 여부에 따라 TodoItem 컨포넌트의 opacity를 조절하도록 수정해본다.
/TodoItem/style.js
export const ItemContainer = styled.div`
${({ theme }) => theme.common.flexAround};
flex-shrink: 0;
background-color: ${({ theme }) => theme.palette.yellow};
opacity: ${({ isChecked }) => (isChecked ? '0.5' : '1')}; /* 추가 */
border-radius: 10px;
color: black;
width: 90%;
height: 5rem;
margin: 0.5rem;
font-family: 'NotoSansBold';
`
/TodoItem/index.jsx
import React from 'react';
import * as S from './style';
import { GrCheckbox, GrCheckboxSelected } from 'react-icons/gr';
import { AiOutlineCloseCircle } from 'react-icons/ai';
function TodoItem({ itemData, todoCheckHandler, todoRemoveHandler }) {
const { id, date, content, checked } = itemData;
return (
<S.ItemContainer isChecked={checked}>
<S.ItemButton onClick={() => todoCheckHandler(id)}>
{checked ? <GrCheckboxSelected /> : <GrCheckbox />}
</S.ItemButton>
<S.ItemText>{date}</S.ItemText>
<S.ItemText>{content}</S.ItemText>
<S.ItemButton onClick={() => todoRemoveHandler(id)}>
<AiOutlineCloseCircle />
</S.ItemButton>
</S.ItemContainer>
);
}
export default TodoItem;
완료한 Todo는 하단으로 배치(sort)
todoData가 변경될 때마다, 자동으로 데이터가 하단으로 소팅되도록 한다.
/TodoList/index.jsx (아래 내용 추가)
const [sortedData, setSortedData] = useState([])
useEffect(() => {
setSortedData(todoData.sort((a, b) => a.checked - b.checked))
}, [todoData])
메가바이트스쿨 FE4기 수업자료를 참고하였습니다.
'React' 카테고리의 다른 글
TodoList 만들기(1) (0) | 2023.04.05 |
---|---|
Styled-components (0) | 2023.03.21 |
useRef (0) | 2023.03.20 |
이벤트핸들러 (0) | 2023.03.15 |
useEffect (0) | 2023.03.09 |