Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1,562 changes: 1,529 additions & 33 deletions package-lock.json

Large diffs are not rendered by default.

7 changes: 7 additions & 0 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -3,12 +3,19 @@
"version": "0.1.0",
"private": true,
"dependencies": {
"@react-three/cannon": "^6.6.0",
"@react-three/drei": "^9.84.2",
"@react-three/fiber": "^8.14.2",
"@testing-library/jest-dom": "^5.16.5",
"@testing-library/react": "^13.4.0",
"@testing-library/user-event": "^13.5.0",
"react": "^18.2.0",
"react-dom": "^18.2.0",
"react-scripts": "5.0.1",
"recoil": "^0.7.7",
"styled-components": "^6.0.8",
"styled-reset": "^4.5.1",
"three": "^0.156.1",
"web-vitals": "^2.1.4"
},
"scripts": {
Expand Down
Binary file modified public/favicon.ico
Binary file not shown.
35 changes: 10 additions & 25 deletions public/index.html
Original file line number Diff line number Diff line change
Expand Up @@ -9,35 +9,20 @@
name="description"
content="Web site created using create-react-app"
/>
<link rel="apple-touch-icon" href="%PUBLIC_URL%/logo192.png" />
<!--
manifest.json provides metadata used when your web app is installed on a
user's mobile device or desktop. See https://developers.google.com/web/fundamentals/web-app-manifest/
-->
<link rel="manifest" href="%PUBLIC_URL%/manifest.json" />
<!--
Notice the use of %PUBLIC_URL% in the tags above.
It will be replaced with the URL of the `public` folder during the build.
Only files inside the `public` folder can be referenced from the HTML.

Unlike "/favicon.ico" or "favicon.ico", "%PUBLIC_URL%/favicon.ico" will
work correctly both with client-side routing and a non-root public URL.
Learn how to configure a non-root public URL by running `npm run build`.
-->
<title>React App</title>
<link rel="preconnect" href="https://fonts.googleapis.com" />
<link rel="preconnect" href="https://fonts.gstatic.com" crossorigin />
<!--구글 Noto sans Korean 폰트 사용 -->
<link rel="preconnect" href="https://fonts.googleapis.com" />
<link rel="preconnect" href="https://fonts.gstatic.com" crossorigin />
<link
href="https://fonts.googleapis.com/css2?family=Noto+Sans+KR:wght@300;400;700&display=swap"
rel="stylesheet"
/>
<title>Universe-Todo</title>
</head>
<body>
<noscript>You need to enable JavaScript to run this app.</noscript>
<div id="root"></div>
<!--
This HTML file is a template.
If you open it directly in the browser, you will see an empty page.

You can add webfonts, meta tags, or analytics to this file.
The build step will place the bundled scripts into the <body> tag.

To begin the development, run `npm start` or `yarn start`.
To create a production bundle, use `npm run build` or `yarn build`.
-->
</body>
</html>
Binary file removed public/logo192.png
Binary file not shown.
Binary file removed public/logo512.png
Binary file not shown.
3 changes: 0 additions & 3 deletions public/robots.txt

This file was deleted.

20 changes: 15 additions & 5 deletions src/App.js
Original file line number Diff line number Diff line change
@@ -1,9 +1,19 @@
import { GlobalStyle } from "./style/GlobalStyle";
import Template from "./components/template/Template";
import Background from "./components/organism/Background";
import { RecoilRoot } from "recoil";

function App() {
return (
<div>
<h1>18기 프론트 화이팅~ 푸하항ㅋ</h1>
</div>
);
return (
<RecoilRoot>
<GlobalStyle />
<>
{/* three.js 을 사용한 우주 배경 */}
<Background />
<Template />
</>
</RecoilRoot>
);
Comment thread
rmdnps10 marked this conversation as resolved.
}

export default App;
14 changes: 14 additions & 0 deletions src/components/atoms/Button.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
import { styled } from "styled-components";

export const Button = styled.div`
display: flex;
width: 67px;
height: 27px;
align-items: center;
justify-content: center;
background-color: var(--lightGray);
border: 2px solid var(--black);
border-radius: 10px;
font-weight: 600;
font-size: 13px;
Comment on lines +3 to +13
Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

px 단위의 작업도 좋지만 모바일에서의 사용성도 고려하여 media-query 와 rem 작업 단위로 작업하는 것을 습관화 하시면 좋을 거 같습니다 ㅎㅎ

`;
5 changes: 5 additions & 0 deletions src/components/atoms/H1.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
import { styled } from "styled-components";
export const H1 = styled.h1`
font-size: 24px;
color: ${(props) => props.color || "var(--white)"};
`;
5 changes: 5 additions & 0 deletions src/components/atoms/ImageButton.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
import { styled } from "styled-components";

export const ImageButton = styled.img`
height: 70%;
`;
9 changes: 9 additions & 0 deletions src/components/atoms/Input.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
import { styled } from "styled-components";

export const Input = styled.input`
width: 70%;
background-color: var(--white);
font-size: 12px;
border: none;
outline: none;
`;
Comment thread
rmdnps10 marked this conversation as resolved.
6 changes: 6 additions & 0 deletions src/components/atoms/Span.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
import { styled } from "styled-components";
export const Span = styled.div`
font-size: 15px;
padding: 5px;
color: ${(props) => props.color || "var(--white)"};
`;
50 changes: 50 additions & 0 deletions src/components/molecules/TextInput.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,50 @@
import React from "react";
import { styled } from "styled-components";
import { ImageButton } from "../atoms/ImageButton";
import { Button } from "../atoms/Button";
import { Input } from "../atoms/Input";
import logo from "../../image/galaxy-icon.svg";
import { useRecoilState } from "recoil";
Comment thread
rmdnps10 marked this conversation as resolved.
import { nowTodoList } from "../../recoil/atom.ts";
import useInput from "../../hooks/useTextInput";

function TextInput() {
const { input, handleChange, handleKeyPress, setInput } = useInput(""); // 키 입력 할 때를 따로 커스텀 훅으로 분리함
const [nowTodo, setNowTodoList] = useRecoilState(nowTodoList);

const buttonClick = () => {
if (input.length < 5 || input.length > 30) {
alert("할 일은 5자 이상 30자 이하로 작성해주세요.");
return;
}
setNowTodoList([...nowTodo, input]);
setInput("");
};

return (
<StyledInput>
<ImageButton src={logo} alt="로고" />
<Input
type="text"
placeholder="화면 중앙을 드래그하거나 포커스 한 후 마우스 휠을 움직여보세요."
value={input}
onChange={handleChange}
onKeyPress={(e) => handleKeyPress(e, buttonClick)}
/>
<Button onClick={buttonClick}>Submit</Button>
Comment thread
rmdnps10 marked this conversation as resolved.
</StyledInput>
);
}

const StyledInput = styled.div`
display: flex;
gap: 20px;
align-items: center;
justify-content: center;
height: 45px;
width: 45%;
border-radius: 20px;
background-color: var(--white);
`;

export default TextInput;
81 changes: 81 additions & 0 deletions src/components/molecules/ToDoItem.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,81 @@
import React from "react";
import { Span } from "../atoms/Span";
import { styled } from "styled-components";
import { ImageButton } from "../atoms/ImageButton";
import deleteButton from "../../image/delete.png";
import backButton from "../../image/restore.png";
import { useRecoilState } from "recoil";
import { nowTodoList, solvedTodoList } from "../../recoil/atom.ts";
function ToDoItem({ todo, isSolved }) {
const [nowTodo, setNowTodo] = useRecoilState(nowTodoList);
const [solvedTodo, setSolvedTodo] = useRecoilState(solvedTodoList);
const deleteButtonClick = () => {
const newSolvedList = solvedTodo.filter((item) => item !== todo);
setSolvedTodo(newSolvedList);
};
const backButtonClick = () => {
const newSolvedList = solvedTodo.filter((item) => item !== todo);
setSolvedTodo(newSolvedList);
setNowTodo([...nowTodo, todo]);
};
const clickNowToDo = (e) => {
// SolvedToDo 아이템 클릭했을 경우, 아무 일도 발생하지 않기하기
if (isSolved) {
return;
} else {
const targetTodo = e.target.innerText;
const newNowTodoList = nowTodo.filter((item) => item !== targetTodo);
setNowTodo(newNowTodoList);
setSolvedTodo([...solvedTodo, targetTodo]);
}
Comment thread
rmdnps10 marked this conversation as resolved.
};
Comment on lines +12 to +31
Copy link
Copy Markdown

@wokbjso wokbjso Sep 23, 2023

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

지극히 제 개인적인 의견입니다만, todo를 삭제하거나 옮기는 과정에서 filter 를 사용하면 현재 있는 todo를 전부 탐색하여 비교하는 과정을 거쳐야하므로 todo가 많이 생긴다면 살짝 비효율적인 면이 있지 않을까 생각합니다.
해당 컴포넌트의 index 를 받아와서 splice 함수로 처리해주는 것도 좋을 거 같다는 생각이 듭니다 ㅎㅎ

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

오 좋은 방법 같아요 저도 배워갑니당

Copy link
Copy Markdown
Author

@rmdnps10 rmdnps10 Sep 24, 2023

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

todo 가 많이 생긴다면 분명히 splice 함수로 처리하는 게 좋은 방법일 것 같네요... 감사합니다!

return (
<>
<StyledItem isSolved={isSolved}>
<Span onClick={clickNowToDo}>{todo}</Span>

{isSolved ? (
<ButtonWrapper>
<ImageButton src={deleteButton} onClick={deleteButtonClick} />
<ImageButton src={backButton} onClick={backButtonClick} />
</ButtonWrapper>
) : (
""
)}
Comment thread
rmdnps10 marked this conversation as resolved.
</StyledItem>
</>
);
}

const StyledItem = styled.div`
display: flex;
justify-content: center;
gap: 20px;
align-items: center;
border-radius: 20px;
padding: 20px 14px;
height: 45px;
background: var(--gradient);
cursor: pointer;
transition: 1s;
// 그림자 잘리는 것 방지를 위한 margin 설정
margin: 10px;
${(props) =>
props.isSolved
? ""
: `
box-shadow: 0px 0px 20px white;
&:hover {
background: yellow;
> ${Span} {
color: black; /* 호버 시 Span의 텍스트 색상을 검은색으로 변경 */
}
}`}
`;
const ButtonWrapper = styled.div`
display: flex;
align-items: center;
height: 40px;
`;

export default ToDoItem;
24 changes: 24 additions & 0 deletions src/components/organism/Background.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,24 @@
import { Canvas } from "@react-three/fiber";
import { OrbitControls, Stars } from "@react-three/drei";
Comment thread
rmdnps10 marked this conversation as resolved.

export default function Background() {
return (
<div
style={{
width: "100%",
height: "100vh",
position: "absolute",
top: "0px",
zIndex: "-999",
}}
>
{/* three.js 라이브러리에서 가져온 컴포넌트 */}
<Canvas resize={{ polyfill: false }}>
<OrbitControls />
<Stars />
<ambientLight intensity={0.5} />
<spotLight position={[10, 15, 10]} angle={0.3} />
</Canvas>
</div>
);
}
Comment thread
rmdnps10 marked this conversation as resolved.
24 changes: 24 additions & 0 deletions src/components/organism/Header.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,24 @@
import React, { useEffect, useRef } from "react";
import { styled } from "styled-components";
import TextInput from "../molecules/TextInput";
import { Span } from "../atoms/Span";

function Header({}) {
Comment thread
rmdnps10 marked this conversation as resolved.
return (
<StyledHeader>
<TextInput />
<Span>"Carl Seagan - 소멸은 법칙이다."</Span>
</StyledHeader>
);
}

const StyledHeader = styled.header`
display: flex;
height: 250px;
align-items: center;
justify-content: center;
flex-direction: column;
gap: 26px;
`;

export default Header;
48 changes: 48 additions & 0 deletions src/components/organism/NowTodo.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,48 @@
import React from "react";
import { H1 } from "../atoms/H1";
import { Span } from "../atoms/Span";
import ToDoItem from "../molecules/ToDoItem";
import { styled } from "styled-components";
import { useRecoilValue, useRecoilState } from "recoil";
import { nowTodoList, solvedTodoList } from "../../recoil/atom.ts";

function NowTodo() {
const nowTodo = useRecoilValue(nowTodoList);
const [solvedToDo, setSolvedTodo] = useRecoilState(solvedTodoList);
return (
<StyledNowTodo>
<H1>✔ 지금 당장 해결하세요.</H1>
<Span color="var(--lightWhite)">
총 {nowTodo.length} 개의 할 일이 있어요.
</Span>
<StyledList>
{nowTodo.map((todo, idx) => (
<ToDoItem
isSolved={false}
todo={todo}
setSolvedTodo={setSolvedTodo}
key={idx}
/>
Comment on lines +19 to +25
Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

map 함수 사용 시 key 를 index 로 설정하는 것은 권장하지 않는다고 합니다.
todo의 값으로 key를 설정하거나 todo의 내용이 중복될 수 있다면 todo+index 로 고유의 key값을 설정하는 것을 추천드립니다~!!
key값으로 index를 단독으로 사용하면 안되는 이유

Suggested change
{nowTodo.map((todo, idx) => (
<ToDoItem
isSolved={false}
todo={todo}
setSolvedTodo={setSolvedTodo}
key={idx}
/>
{nowTodo.map((todo, idx) => (
<ToDoItem
isSolved={false}
todo={todo}
setSolvedTodo={setSolvedTodo}
key={todo+idx}
/>

))}
</StyledList>
</StyledNowTodo>
);
}

// SolvedTodo.js에서 사용하기 위함
export const StyledList = styled.div`
display: flex;
margin-top: 20px;
overflow: scroll;
flex-direction: column;
align-items: start;
gap: 15px;
height: 500px;
`;
Comment on lines +33 to +41
Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

이렇게 export하는 것보단 따로 component를 만들고 NowTodo, SolvedTodo에서 사용하는건 어떨까요 ??


const StyledNowTodo = styled.div`
position: absolute;
top: 170px;
left: 50px;
`;
export default NowTodo;
Loading