Sun's Blog
React Todo 리스트 컴포넌트편 본문
프로젝트 생성
npx create-react-app todolist
의존성 추가
- react-icons: npm i react-icons
- styled-components: npm i styled-components
페이지에 회색 배경 적용
App.js
import React from "react";
import { createGlobalStyle } from "styled-components";
const GlobalStyle = createGlobalStyle`
body {
background: #e9ecef;
}
`;
function App() {
return (
<>
<GlobalStyle />
<div>안녕하세요</div>
</>
);
}
export default App;
- createGlobalStyle: styled-components에서 특정 컴포넌트를 통해 글로벌 스타일을 설정
Todo Template
투두리스트의 레이아웃 설정, 페이지 중앙에 그림자가 적용된 흰색 박스
components/ToDoTemplate.js
import React from "react";
import styled from "styled-components";
const TodoTemplateBlock = styled.div`
width: 512px;
height: 768px;
position: relative; /* 추후 박스 하단에 추가 버튼을 위치시키기 위한 설정 */
background: white;
border-radius: 16px;
box-shadow: 0 0 8px 0 rgba(0, 0, 0, 0.04);
margin: 0 auto; /* 페이지 중앙에 나타나도록 설정 */
margin-top: 96px;
margin-bottom: 32px;
display: flex;
flex-direction: column;
`;
function TodoTemplate({children}) {
return <TodoTemplateBlock>{children}</TodoTemplateBlock>
}
export default TodoTemplate;
App.js
import React from "react";
import { createGlobalStyle } from "styled-components";
import TodoTemplate from "./components/TodoTemplate";
const GlobalStyle = createGlobalStyle`
body {
background: #e9ecef;
}
`;
function App() {
return (
<>
<GlobalStyle />
<TodoTemplate>TodoTemplate적용</TodoTemplate>
</>
);
}
export default App;
화면
TodoHead
오늘 날짜와 앞으로 남은 일의 갯수를 알려준다.
import React from "react";
import styled from "styled-components";
const TodoHeadBlock = styled.div `
padding-top: 48px;
padding-left: 32px;
padding-right: 32px;
padding-bottom: 24px;
border-bottom: 1px solid #e9ecef;
h1 {
margin: 0;
font-size: 36px;
color: #343a40;
}
.day {
margin-top: 4px;
color: #868e96;
font-size: 21px;
}
.tasks-left {
color: #20c997;
font-size: 18px;
margin-top: 40px;
font-weight: bold;
}
`;
function TodoHead() {
return (
<TodoHeadBlock>
<h1>2023-02-20</h1>
<div className="day">화요일</div>
<div className="tasks-left">할 일 2개 남음</div>
</TodoHeadBlock>
)
}
export default TodoHead;
App.js
import React from "react";
import { createGlobalStyle } from "styled-components";
import TodoTemplate from "./components/TodoTemplate";
import TodoHead from "./components/TodoHead";
const GlobalStyle = createGlobalStyle`
body {
background: #e9ecef;
}
`;
function App() {
return (
<>
<GlobalStyle />
<TodoTemplate>
<TodoHead />
</TodoTemplate>
</>
);
}
export default App;
화면
TodoList
할일이 들어있는 List 관리
TodoList.js
import React from "react";
import styled from "styled-components";
const TodoListBlock = styled.div `
flex: 1;
padding: 20px 32px;
padding-bottom: 48px;
overflow-y: auto;
background: gray; /* 사이즈 조정이 잘 되고 있는지 확인하기 위한 임시 스타일 */
`
function TodoList() {
return <TodoListBlock>TodoList</TodoListBlock>
}
export default TodoList;
- 현재 들어있는 아이템이 없기 때문에 사이즈 확인을 위한 background: gray를 설정
App.js
import React from "react";
import { createGlobalStyle } from "styled-components";
import TodoTemplate from "./components/TodoTemplate";
import TodoHead from "./components/TodoHead";
import TodoList from "./components/TodoList";
const GlobalStyle = createGlobalStyle`
body {
background: #e9ecef;
}
`;
function App() {
return (
<>
<GlobalStyle />
<TodoTemplate>
<TodoHead />
<TodoList />
</TodoTemplate>
</>
);
}
export default App;
화면
- TodoTemplate의 하얀 배경에 꽉찬 회색을 확인했으면 background: gray를 삭제
TodoItem
각 할일을 렌더링해주는 컴포넌트, 클릭 시 완료 처리 및 우측의 아이콘 클릭 시 해당 아이템 삭제
TodoItem.js
import React from "react";
import styled, {css} from "styled-components";
import {MdDone, MdDelete} from 'react-icons/md'
const Remove = styled.div`
display: flex;
align-items: center;
justify-content: center;
color: #dee2e6;
font-size: 24px;
cursor: pointer;
&:hover {
color: #ff6b6b;
}
display: none;
`;
const TodoItemBlock = styled.div`
display: flex;
align-items: center;
padding-top: 12px;
padding-bottom: 12px;
&:hover {
${Remove} {
display: initial;
}
}
`;
const CheckCircle = styled.div`
width: 32px;
height: 32px;
border-radius: 16px;
border: 1px solid #ced4da;
font-size: 24px;
display: flex;
align-items: center;
justify-content: center;
margin-right: 20px;
cursor: pointer;
${props =>
props.done &&
css`
border: 1px solid #38d9a9;
color: #38d9a9;
`}
`;
const Text = styled.div`
flex: 1;
font-size: 21px;
color: #495057;
${props =>
props.done &&
css`
color: #ced4da;
`}
`;
function TodoItem({id, done, text}) {
return (
<TodoItemBlock>
<CheckCircle done={done} > {done && <MdDone />}</CheckCircle>
<Text done={done}> {text}</Text>
<Remove>
<MdDelete />
</Remove>
</TodoItemBlock>
);
}
export default TodoItem;
- ${remove}나 ${props}는 Component Selector 라는 기능이다.
TodoList
import React from "react";
import styled from "styled-components";
import TodoItem from "./TodoItem";
const TodoListBlock = styled.div `
flex: 1;
padding: 20px 32px;
padding-bottom: 48px;
overflow-y: auto;
`
function TodoList() {
return <TodoListBlock>
<TodoItem text="프로젝트 생성하기" done={true} />
<TodoItem text="컴포넌트 스타일링 하기" done={true} />
<TodoItem text="Context 만들기" done={false} />
<TodoItem text="기능 구현하기" done={false} />
</TodoListBlock>
}
export default TodoList;
화면
TodoCreate
새로운 할일을 등록하는 컴포넌트
TodoCreate.js
import React, {useState} from "react";
import styled, {css} from "styled-components";
import { MdAdd } from "react-icons/md";
const CircleButton = styled.button`
background: #38d9a9;
&:hover {
background: #63e6be;
}
&:active {
background: #20c997;
}
z-index: 5;
cursor: pointer;
width: 80px;
height: 80px;
display: block;
align-items: center;
justify-content: center;
font-size: 60px;
position: absolute;
left: 50%;
bottom: 0px;
transform: translate(-50%, 50%);
color: white;
border-radius: 50%;
border: none;
outline: none;
display: flex;
align-items: center;
justify-content: center;
transition: 0.125s all ease-in;
${props =>
props.open &&
css`
background: #ff6b6b;
&:hover {
background: #ff8787;
}
&:active {
background: #fa5252;
}
transform: translate(-50%, 50%) rotate(45deg);
`}
`;
const InsertFormPositioner = styled.div`
width: 100%;
bottom: 0;
left: 0;
position: absolute;
`;
const InsertForm = styled.form`
background: #f8f9fa;
padding-left: 32px;
padding-top: 32px;
padding-right: 32px;
padding-bottom: 72px;
border-bottom-left-radius: 16px;
border-bottom-right-radius: 16px;
border-top: 1px solid #e9ecef;
`;
const Input = styled.input`
padding: 12px;
border-radius: 4px;
border: 1px solid #dee2e6;
width: 100%;
outline: none;
font-size: 18px;
box-sizing: border-box;
`;
function TodoCreate() {
const [open, setOpen] = useState(false);
const onToggle = () => setOpen(!open);
return (
<>
{open && (
<InsertFormPositioner>
<InsertForm>
<Input autoFocus placeholder="할 일을 입력 후, Enter 를 누르세요" />
</InsertForm>
</InsertFormPositioner>
)}
<CircleButton onClick={onToggle} open={open}>
<MdAdd />
</CircleButton>
</>
)
}
export default TodoCreate;
- open의 상태값을 state로 관리하여 input 창을 보여줌과 + 버튼의 css를 관리한다.
App.js
import React from "react";
import { createGlobalStyle } from "styled-components";
import TodoTemplate from "./components/TodoTemplate";
import TodoHead from "./components/TodoHead";
import TodoList from "./components/TodoList";
import TodoCreate from "./components/TodoCreate";
const GlobalStyle = createGlobalStyle`
body {
background: #e9ecef;
}
`;
function App() {
return (
<>
<GlobalStyle />
<TodoTemplate>
<TodoHead />
<TodoList />
<TodoCreate />
</TodoTemplate>
</>
);
}
export default App;
화면
참고
https://react.vlpt.us/mashup-todolist/01-create-components.html
'ETC' 카테고리의 다른 글
React 라우터 (0) | 2024.02.25 |
---|---|
React CSS Module (0) | 2024.02.18 |
node-sass 사용해보기 (0) | 2024.02.18 |
React 4일차: state (1) | 2024.02.04 |
Tiles (0) | 2024.02.04 |