Skip to content

Commit

Permalink
[feat] 토픽 공유 기능 구현 (#65)
Browse files Browse the repository at this point in the history
* feat: ShareIcon 구현

* feat: ShareIcon 적용
  • Loading branch information
hwookim authored Jan 26, 2023
1 parent 9ee2354 commit c4d077e
Show file tree
Hide file tree
Showing 9 changed files with 124 additions and 8 deletions.
9 changes: 9 additions & 0 deletions src/assets/icon/emoji_smile.svg
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
1 change: 1 addition & 0 deletions src/assets/icon/index.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,7 @@ export { ReactComponent as Computer } from './emoji_computer.svg';
export { ReactComponent as HandsUp } from './emoji_hands_up.svg';
export { ReactComponent as Paint } from './emoji_paint.svg';
export { ReactComponent as Paper } from './emoji_paper.svg';
export { ReactComponent as Smile } from './emoji_smile.svg';
export { ReactComponent as ThumbsUp } from './emoji_thumbs_up.svg';
export { ReactComponent as Image } from './image.svg';
export { ReactComponent as Logo } from './logo.svg';
Expand Down
26 changes: 26 additions & 0 deletions src/components/common/ShareIcon/ShareIcon.stories.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,26 @@
import styled from '@emotion/styled';
import { ComponentMeta, ComponentStory } from '@storybook/react';

import ShareIcon from '.';

export default {
title: 'common/ShareIcon',
component: ShareIcon,
argTypes: {},
} as ComponentMeta<typeof ShareIcon>;

const Container = styled.div`
display: flex;
justify-content: end;
`;

const Template: ComponentStory<typeof ShareIcon> = (args) => (
<Container>
<ShareIcon {...args} />
</Container>
);

export const Default = Template.bind({});
Default.args = {
url: 'http://localhost:3000/topics/1',
};
35 changes: 35 additions & 0 deletions src/components/common/ShareIcon/ShareIcon.styles.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,35 @@
import styled from '@emotion/styled';

import Icon from '@src/components/common/Icon';
import theme from '@src/styles/theme';

export const Container = styled.div`
width: 24px;
height: 24px;
position: relative;
`;

export const Share = styled(Icon)`
cursor: pointer;
`;

export const Toast = styled.div`
display: flex;
justify-content: center;
align-items: center;
gap: 2px;
position: absolute;
width: 200px;
height: 48px;
top: calc(100% + 8px);
right: 0;
background-color: ${theme.color.G6};
border-radius: 8px;
font-weight: ${theme.fontWeight.bold};
font-size: ${theme.textSize.T3};
line-height: ${theme.lineHeight.B};
`;
42 changes: 42 additions & 0 deletions src/components/common/ShareIcon/ShareIcon.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,42 @@
import React, { MouseEvent, useState } from 'react';

import Icon from '@src/components/common/Icon';

import * as S from './ShareIcon.styles';

interface ShareIconProps {
url: string;
}

const ShareIcon: React.FC<ShareIconProps> = (props) => {
const { url } = props;
const [isCopied, setIsCopied] = useState<boolean>(false);

const handleShare = async (e: MouseEvent<SVGSVGElement>) => {
e.stopPropagation();
e.nativeEvent.preventDefault();

if (isCopied) return;
if (!navigator.clipboard) return;

await navigator.clipboard.writeText(url);
setIsCopied(true);
setTimeout(() => {
setIsCopied(false);
}, 1000);
};

return (
<S.Container>
<S.Share name="Share" onClick={handleShare} />
{isCopied && (
<S.Toast>
<Icon name="Smile" />
링크가 복사되었습니다!
</S.Toast>
)}
</S.Container>
);
};

export default ShareIcon;
1 change: 1 addition & 0 deletions src/components/common/ShareIcon/index.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
export { default } from './ShareIcon';
5 changes: 3 additions & 2 deletions src/components/common/TopicCard/TopicCard.tsx
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
import React, { useState } from 'react';

import ShareIcon from '@src/components/common/ShareIcon';
import Topic, { TopicOption } from '@src/types/Topic';

import Icon from '../Icon';
Expand All @@ -15,7 +16,7 @@ interface TopicCardProps extends Topic {
}

const TopicCard = (props: TopicCardProps, ref: React.ForwardedRef<HTMLDivElement>) => {
const { title, contents, options: defaultOptions, member, comments, badge, type, onClick } = props;
const { id, title, contents, options: defaultOptions, member, comments, badge, type, onClick } = props;
const [options, setOptions] = useState<TopicOption[]>(defaultOptions);
const [selectedOptionId, setSelectedOptionId] = useState<number | null>(null); // TODO: 초기 선택 여부 확인해야함

Expand Down Expand Up @@ -56,7 +57,7 @@ const TopicCard = (props: TopicCardProps, ref: React.ForwardedRef<HTMLDivElement
)}
<S.Title>{title}</S.Title>
</div>
{isFeed && <Icon name="Share" />}
{isFeed && <ShareIcon url={`${location.host}/topics/${id}`} />}
</S.TopicHeader>
<S.Contents>{contents}</S.Contents>
<S.SelectOptionContainer $odd={options.length % 2 === 1}>
Expand Down
Original file line number Diff line number Diff line change
@@ -1,7 +1,5 @@
import styled from '@emotion/styled';

import Icon from '@src/components/common/Icon';

export const Wrapper = styled.main`
margin-top: -20px;
display: flex;
Expand All @@ -15,7 +13,7 @@ export const TopicCardWrapper = styled.div`
gap: 12px;
`;

export const ShareIcon = styled(Icon)`
align-self: flex-end;
cursor: pointer;
export const ShareIcon = styled.div`
display: flex;
justify-content: end;
`;
5 changes: 4 additions & 1 deletion src/components/topic/TopicDetailMain/TopicDetailMain.tsx
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
import { FC } from 'react';

import { Topic } from '@src/apis';
import ShareIcon from '@src/components/common/ShareIcon';
import TopicCard from '@src/components/common/TopicCard';
import CommentForm from '@src/components/topic/CommentForm';
import CommentList from '@src/components/topic/CommentList';
Expand All @@ -24,7 +25,9 @@ const TopicDetailMain: FC<Props> = (props) => {
return (
<S.Wrapper>
<S.TopicCardWrapper>
<S.ShareIcon name="Share" size={24} />
<S.ShareIcon>
<ShareIcon url={location.href} />
</S.ShareIcon>
<TopicCard title={title} contents={contents} options={voteOptions} comments={commentAmount} type={'detail'} />
</S.TopicCardWrapper>
<CommentForm placeholder="닉네임님, 댓글을 남겨보세요! 💬" onSubmit={handleAddComment} />
Expand Down

0 comments on commit c4d077e

Please sign in to comment.