Skip to content

Commit

Permalink
[#23] Feed UI작업
Browse files Browse the repository at this point in the history
  • Loading branch information
dongha authored and kdongha committed Apr 12, 2021
1 parent fa25783 commit 523c2d6
Show file tree
Hide file tree
Showing 22 changed files with 424 additions and 36 deletions.
1 change: 1 addition & 0 deletions .eslintrc.json
Original file line number Diff line number Diff line change
Expand Up @@ -37,6 +37,7 @@
"import/prefer-default-export": 0,
"import/newline-after-import": 0,
"react/jsx-props-no-spreading": 0,
"no-shadow": 0,
"no-unused-vars": 0,
"import/extensions": [
"error",
Expand Down
25 changes: 14 additions & 11 deletions components/Carousel/index.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -24,28 +24,28 @@ function Carousel({ images, showDots }: CarouselType): ReactElement {
slideRef.current.style.transform = `translateX(-${currentSlide}00%)`;
}, [slideRef, currentSlide]);
return (
<>
<Container>
<SliderContainer ref={slideRef}>
<Container>
<SliderContainer>
<ImageContainer ref={slideRef}>
{images.map((image) => (
<Img src={image} alt={image} />
))}
</SliderContainer>
</ImageContainer>
{hasPrev && <Button onClick={handleClickPrev}>&lt;</Button>}
{hasNext && (
<Button align="right" onClick={handleClickNext}>
&gt;
</Button>
)}
</Container>
</SliderContainer>
{showDots && (
<DotsContainer>
{images.map((_, index) => (
<Dot active={currentSlide === index} />
))}
</DotsContainer>
)}
</>
</Container>
);
}

Expand All @@ -55,13 +55,15 @@ Carousel.defaultProps = {

export default Carousel;

const Container = styled.div`
const Container = styled.div``;

const SliderContainer = styled.div`
width: 100%;
overflow-x: hidden;
position: relative;
`;

const SliderContainer = styled.div`
const ImageContainer = styled.div`
display: flex;
transition: all 0.3s ease-in-out;
`;
Expand Down Expand Up @@ -92,9 +94,10 @@ const Button = styled.button<ButtonType>`
`;

const DotsContainer = styled.div`
text-align: center;
height: 20px;
margin-bottom: -20px;
display: flex;
justify-content: center;
align-items: center;
height: 40px;
`;

type DotType = {
Expand Down
22 changes: 22 additions & 0 deletions components/Feed/Anchor.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,22 @@
import React, { ReactElement, ReactNode } from 'react';
import styled from '@emotion/styled';
import Link, { LinkProps } from 'next/link';

export type AnchorProps = {
children: ReactNode;
} & LinkProps;

function Anchor({ children, ...linkProps }: AnchorProps): ReactElement {
return (
<Link {...linkProps}>
<StyledAnchor>{children}</StyledAnchor>
</Link>
);
}

export default Anchor;

const StyledAnchor = styled.a`
cursor: pointer;
font-weight: 600;
`;
56 changes: 56 additions & 0 deletions components/Feed/ButtonGroup.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,56 @@
import React, { ReactElement } from 'react';
import Icon from 'components/Icon';
import styled from '@emotion/styled';

export type ButtonGroupProps = {
isLike: boolean;
};

function ButtonGroup({ isLike }: ButtonGroupProps): ReactElement {
return (
<Container>
<IconButton>
<Icon name={isLike ? 'likeFilled' : 'like'} size="big" />
</IconButton>
<IconButton>
<Icon name="comment" size="big" />
</IconButton>
<IconButton>
<Icon name="share" size="big" />
</IconButton>
<FlexGap />
<IconButton>
<Icon name="bookmark" size="big" />
</IconButton>
</Container>
);
}

export default ButtonGroup;

const IconButton = styled.button`
all: unset;
height: 40px;
width: 40px;
padding: 8px;
box-sizing: border-box;
cursor: pointer;
`;

const Container = styled.section`
height: 40px;
width: 100%;
margin-top: -40px;
display: flex;
align-items: center;
${IconButton}:first-child {
margin-left: -8px;
}
${IconButton}:last-child {
margin-right: -8px;
}
`;

const FlexGap = styled.div`
flex-grow: 1;
`;
16 changes: 16 additions & 0 deletions components/Feed/CommentMore.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,16 @@
import React, { ReactElement } from 'react';
import styled from '@emotion/styled';

export type CommentMoreProps = {
commentLength: number;
};

function CommentMore({ commentLength }: CommentMoreProps): ReactElement {
return <Container>댓글 {commentLength}개 모두 보기</Container>;
}

export default CommentMore;

const Container = styled.div`
margin-bottom: 4px;
`;
19 changes: 19 additions & 0 deletions components/Feed/CommentPreview.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,19 @@
import React, { ReactElement } from 'react';
import { PreviewComment } from 'types/index';
import Anchor from 'components/Feed/Anchor';
import styled from '@emotion/styled';

function CommentPreview({ author: { displayId }, content }: PreviewComment): ReactElement {
return (
<Container>
<Anchor href={`/${displayId}`}>{displayId}</Anchor>
&nbsp;{content}
</Container>
);
}

export default CommentPreview;

const Container = styled.div`
margin-bottom: 4px;
`;
24 changes: 24 additions & 0 deletions components/Feed/ContentBody.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,24 @@
import React, { ReactElement } from 'react';
import { FeedAuthor } from 'types/index';
import Anchor from 'components/Feed/Anchor';
import styled from '@emotion/styled';

export type AuthorBodyProps = {
body: string;
author: FeedAuthor;
};

function ContentBody({ body, author: { displayId } }: AuthorBodyProps): ReactElement {
return (
<Container>
<Anchor href={`/${displayId}`}>{displayId}</Anchor>
&nbsp;{body}
</Container>
);
}

export default ContentBody;

const Container = styled.div`
margin-bottom: 4px;
`;
64 changes: 64 additions & 0 deletions components/Feed/Feed.stories.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,64 @@
import React from 'react';
import { Story, Meta } from '@storybook/react/types-6-0';

import Feed, { FeedProps } from './Feed';

export default {
title: 'Feed',
component: Feed,
parameters: {
layout: 'fullscreen',
},
} as Meta;

const Template: Story<FeedProps> = (args) => <Feed {...args} />;

export const Default = Template.bind({});
Default.args = {
author: {
displayId: '_dohakim',
isFollowedByMe: true,
},
images: [
'https://via.placeholder.com/300?text=1',
'https://via.placeholder.com/300?text=2',
'https://via.placeholder.com/300?text=3',
'https://via.placeholder.com/300?text=4',
'https://via.placeholder.com/300?text=5',
'https://via.placeholder.com/300?text=6',
'https://via.placeholder.com/300?text=7',
'https://via.placeholder.com/300?text=8',
'https://via.placeholder.com/300?text=9',
'https://via.placeholder.com/300?text=10',
],
isLike: true,
likeLength: 1,
likeUser: {
displayId: 'dongha',
profileImageUrl:
'https://post-phinf.pstatic.net/MjAxODA3MTlfMTIg/MDAxNTMxOTg5ODE5OTAw.edb-H-Rmhr2dFvKAqKA11flZ2k45cRi4Q4IaHirlMF4g.It6ziXN3vtf0R7B2p9DdwOy1hovG7aynuCPwAysStMcg.JPEG/jy180719b2.jpg?type=w1200',
isFollowedByMe: false,
},
body: 'in 송도',
commentLength: 2,
commentPreview: [
{
author: {
displayId: 'dongha',
profileImageUrl:
'https://post-phinf.pstatic.net/MjAxODA3MTlfMTIg/MDAxNTMxOTg5ODE5OTAw.edb-H-Rmhr2dFvKAqKA11flZ2k45cRi4Q4IaHirlMF4g.It6ziXN3vtf0R7B2p9DdwOy1hovG7aynuCPwAysStMcg.JPEG/jy180719b2.jpg?type=w1200',
isFollowedByMe: false,
},
content: '와 멋지다',
},
{
author: {
displayId: 'dongha',
profileImageUrl:
'https://post-phinf.pstatic.net/MjAxODA3MTlfMTIg/MDAxNTMxOTg5ODE5OTAw.edb-H-Rmhr2dFvKAqKA11flZ2k45cRi4Q4IaHirlMF4g.It6ziXN3vtf0R7B2p9DdwOy1hovG7aynuCPwAysStMcg.JPEG/jy180719b2.jpg?type=w1200',
isFollowedByMe: false,
},
content: '와 멋지다2',
},
],
};
39 changes: 39 additions & 0 deletions components/Feed/Feed.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,39 @@
import React from 'react';
import type { ReactElement } from 'react';
import type { Feed as FeedType } from 'types/index';
import Carousel from 'components/Carousel';
import FeedBody from 'components/Feed/FeedBody';
import FeedHeader from './FeedHeader';

export type FeedProps = FeedType;

function Feed({
author,
body,
commentLength,
commentPreview,
createdAt,
images,
isLike,
likeLength,
likeUser,
}: FeedProps): ReactElement {
return (
<div>
<FeedHeader author={author} />
<Carousel images={images} showDots />
<FeedBody
isLike={isLike}
likeUser={likeUser}
likeLength={likeLength}
body={body}
commentLength={commentLength}
commentPreview={commentPreview}
createdAt={createdAt}
author={author}
/>
</div>
);
}

export default Feed;
54 changes: 54 additions & 0 deletions components/Feed/FeedBody.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,54 @@
import React, { ReactElement } from 'react';
import styled from '@emotion/styled';
import ButtonGroup from 'components/Feed/ButtonGroup';
import LikeSection from 'components/Feed/LikeSection';
import { FeedAuthor, PreviewComment } from 'types/index';
import ContentBody from 'components/Feed/ContentBody';
import CommentPreview from 'components/Feed/CommentPreview';

export type FeedBodyProps = {
isLike: boolean;
likeUser: FeedAuthor;
likeLength: number;
author: FeedAuthor;
body: string;
commentLength: number;
commentPreview: PreviewComment[];
createdAt: string;
};

function FeedBody({
isLike,
likeUser,
likeLength,
body,
author,
commentLength,
commentPreview,
}: FeedBodyProps): ReactElement {
return (
<Container>
<ButtonGroup isLike={isLike} />
{likeLength > 0 && <LikeSection likeUser={likeUser} likeLength={likeLength} />}
<ContentBody body={body} author={author} />
{commentLength > 0 && <CommentLengthContainer>댓글 {commentLength}개 모두 보기</CommentLengthContainer>}
{commentPreview.map((previewComment) => (
<CommentPreview {...previewComment} />
))}
</Container>
);
}

export default FeedBody;

const Container = styled.div`
display: flex;
padding: 0 16px;
flex-direction: column;
font-size: 14px;
`;

const CommentLengthContainer = styled.div`
color: rgb(142, 142, 142);
margin-bottom: 4px;
`;
Loading

0 comments on commit 523c2d6

Please sign in to comment.