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
26 changes: 26 additions & 0 deletions week04/youkyeong/Blog/src/component/list/CommentList.jsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,26 @@
import React from "react";
import styled from "styled-components";
import CommentListItem from "./CommentListItem";

const Wrapper = styled.div`
display: flex;
flex-direction: column;
align-items: flex-start;
justify-content: center;

:not(:last-child) {
margin-bottom: 16px;
}
`;

function CommentList({ comments = [] }) {
return (
<Wrapper>
{comments.map((comment, index) => {
return <CommentListItem key={comment.id} comment={comment} />;
})}
</Wrapper>
);
}

export default CommentList;
34 changes: 34 additions & 0 deletions week04/youkyeong/Blog/src/component/list/CommentListItem.jsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,34 @@
import React from "react";
import styled from "styled-components";

const Wrapper = styled.div`
width: calc(100%-32px);
padding: 8px 16px;
display: flex;
flex-direction: column;
align-items: flex-start;
justify-content: center;
border: 1px solid gray;
border-radius: 8px;
cursor: pointer;
background: white;
:hover {
background: lightgrey;
}
`;

const ContentText = styled.p`
font-size: 16px;
white-space: pre-wrap;
`;

function CommentListItem(props) {
const { comment } = props;
return (
<Wrapper>
<ContentText>{comment.content}</ContentText>
</Wrapper>
);
}

export default CommentListItem;
37 changes: 37 additions & 0 deletions week04/youkyeong/Blog/src/component/list/PostList.jsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,37 @@
import React from "react";
import styled from "styled-components";
import PostListItem from "./PostListItem";

const Wrapper = styled.div`
display: flex;
flex-direction: column;
align-items: flex-start;
justify-content: center;

& {
:not(:last-child) {
margin-bottom: 16px;
}
}
`;

function PostList(props) {
const { posts, onClickItem } = props;
return (
<Wrapper>
{posts.map((post, index) => {
return (
<PostListItem
key={post.id}
post={post}
onClick={() => {
onClickItem(post);
}}
/>
);
})}
</Wrapper>
);
}

export default PostList;
34 changes: 34 additions & 0 deletions week04/youkyeong/Blog/src/component/list/PostListItem.jsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,34 @@
import React from "react";
import styled from "styled-components";

const Wrapper = styled.div`
width: 100%;
padding: 16px;
display: flex;
flex-direction: column;
align-items: flex-start;
justify-content: center;
border: 1px solid #8fcefc;
border-radius: 8px;
cursor: pointer;
background-color: white;
:hover {
background: #eef5ff;
}
`;

const Titletext = styled.p`
font-size: 20px;
font-weight: 500;
`;

function PostListItem(props) {
const { post, onClick } = props;
return (
<Wrapper onClick={onClick}>
<Titletext>{post.title}</Titletext>
</Wrapper>
);
}

export default PostListItem;
50 changes: 50 additions & 0 deletions week04/youkyeong/Blog/src/component/page/MainPage.jsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,50 @@
import React from "react";
import { useNavigate } from "react-router-dom";
import styled from "styled-components";
import PostList from "../list/PostList";
import Button from "../ui/Button";
import data from "../../data.json";

const Wrapper = styled.div`
padding: 16px;
width: calc(100%-32px);
display: flex;
flex-direction: column;
align-items: center;
justify-content: center;
`;

const Contatiner = styled.div`
width: 100%;
max-width: 720px;

& > * {
margin-bottom: 16px;
}
`;

function MainPage(props) {
const {} = props;
const navigate = useNavigate();

return (
<Wrapper>
<Contatiner>
<Button
title="글 작성하기"
onClick={() => {
navigate("/post-write");
}}
/>
<PostList
posts={data}
onClickItem={(item) => {
navigate(`/post/${item.id}`);
}}
/>
</Contatiner>
</Wrapper>
);
}

export default MainPage;
95 changes: 95 additions & 0 deletions week04/youkyeong/Blog/src/component/page/PostViewPage.jsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,95 @@
import React, { useState } from "react";
import styled from "styled-components";
import Button from "../ui/Button";
import TextInput from "../ui/TextInput";
import CommentList from "../list/CommentList";
import { useNavigate, useParams } from "react-router-dom";
import data from "../../data.json";

const Wrapper = styled.div`
padding: 16px;
width: calc(100%-32px);
display: flex;
flex-direction: column;
align-items: center;
justify-content: center;
`;

const Container = styled.div`
width: 100%;
max-width: 720px;

:not(:last-child) {
margin-bottom: 16px;
}
`;

const PostContainer = styled.div`
padding: 8px 16px;
border: 1px solid grey;
border-radius: 8px;
`;

const TitleText = styled.p`
font-size: 23px;
font-weight: 500;
`;

const ContextText = styled.p`
font-size: 16px;
line-height: 32px;
white-space: pre-wrap;
`;

const CommentLabel = styled.p`
font-size: 16px;
font-weight: 500;
`;

function PostViewPage(props) {
const [comment, setComment] = useState("");
const navigate = useNavigate();

const { postId } = useParams();

const post = data.find((item) => item.id == postId);

if (!post) {
return <div>포스트를 찾을 수 없습니다.</div>;
}

return (
<Wrapper>
<Container>
<Button
title="뒤로가기"
onClick={() => {
navigate("/");
}}
/>

<PostContainer>
<TitleText>{post.title}</TitleText>
<ContextText>{post.content}</ContextText>
</PostContainer>

<CommentLabel>💬댓글</CommentLabel>
<CommentList comments={post.comments} />

<TextInput
height={40}
value={comment}
onChange={(e) => setComment(e.target.value)}
/>
<Button
title="댓글 작성하기"
onClick={() => {
setComment("");
}}
/>
</Container>
</Wrapper>
);
}

export default PostViewPage;
58 changes: 58 additions & 0 deletions week04/youkyeong/Blog/src/component/page/PostWritePage.jsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,58 @@
import React, { useState } from "react";
import TextInput from "../ui/TextInput";
import Button from "../ui/Button";
import { useNavigate } from "react-router-dom";
import styled from "styled-components";

const Wrapper = styled.div`
display: flex;
flex-direction: column;
align-items: center;
justify-content: center;
`;
const Container = styled.div`
width: 100%;
max-width: 720px;

:not(:last-child) {
margin-bottom: 16px;
}
`;

function PostWritePage(props) {
const navigate = useNavigate();

const [title, setTitle] = useState("");
const [content, setContent] = useState("");

return (
<Wrapper>
<Container>
<TextInput
height={20}
value={title}
onChange={(e) => {
setTitle(e.target.value);
}}
placeholder={"제목을 입력하세요"}
/>
<TextInput
height={480}
value={content}
onChange={(e) => {
setContent(e.target.value);
}}
placeholder={"내용을 입력하세요"}
/>
<Button
title="글 작성하기"
onClick={() => {
navigate("/");
}}
></Button>
</Container>
</Wrapper>
);
}

export default PostWritePage;
19 changes: 19 additions & 0 deletions week04/youkyeong/Blog/src/component/ui/Button.jsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,19 @@
import React from "react";
import styled from "styled-components";

const StyledButton = styled.button`
padding: 8px 16px;
font-size: 16px;
border-width: 1px;
border-radius: 8px;
color: white;
font-weight: 500;
background-color: #8cc6ff;
border: 1px solid white;
cursor: pointer;
`;

export default function Button(props) {
const { title, onClick } = props;
return <StyledButton onClick={onClick}>{title || "button"}</StyledButton>;
}
27 changes: 27 additions & 0 deletions week04/youkyeong/Blog/src/component/ui/TextInput.jsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,27 @@
import React from "react";
import styled from "styled-components";

const StyledTextarea = styled.textarea`
width: calc(100% - 32px);
${(props) => props.height && `height:${props.height}px;`}
padding:16px;
font-size: 16px;
line-height: 20px;
border: 1px solid #8fcefc;
border-radius: 8px;
&::placeholder {
color: #d8d8d8;
}
`;

export default function TextInput(props) {
const { height, value, onChange, placeholder } = props;
return (
<StyledTextarea
height={height}
value={value}
onChange={onChange}
placeholder={placeholder}
/>
);
}