Skip to content

Commit

Permalink
Merge pull request #268 from woowacourse-teams/feat/#240
Browse files Browse the repository at this point in the history
채팅 추가 컴포넌트 및 페이지 구현
  • Loading branch information
cys4585 authored Aug 8, 2024
2 parents d33be51 + 3b9e748 commit 4c15bec
Show file tree
Hide file tree
Showing 42 changed files with 1,055 additions and 179 deletions.
4 changes: 2 additions & 2 deletions frontend/src/common/theme/coloredTypography.ts
Original file line number Diff line number Diff line change
@@ -1,13 +1,13 @@
import { ColoredTypography, Typography } from './theme.type';
import { SerializedStyles, css } from '@emotion/react';

import { Entries } from '@_types/index';
import { css } from '@emotion/react';
import typography from './typography';

const coloredTypography: ColoredTypography = (
Object.entries(typography) as Entries<Typography>
).reduce((object, [key, style]) => {
object[key] = (fontColor: string) => {
object[key] = (fontColor: string | SerializedStyles) => {
return css`
${style}
color:${fontColor};
Expand Down
6 changes: 4 additions & 2 deletions frontend/src/common/theme/theme.style.ts
Original file line number Diff line number Diff line change
@@ -1,12 +1,14 @@
import { Theme } from './theme.type';
import typography from './typography';
import colorPalette from './colorPalette';
import semantic from './semantic';
import coloredTypography from './coloredTypography';
import layout from './layout';
import semantic from './semantic';
import typography from './typography';

export const theme: Theme = {
colorPalette,
typography,
semantic,
layout,
coloredTypography,
};
53 changes: 27 additions & 26 deletions frontend/src/common/theme/theme.type.ts
Original file line number Diff line number Diff line change
Expand Up @@ -11,9 +11,9 @@ export interface Colors {
}

export interface Semantic {
primary: string;
secondary: string;
disabled: string;
primary: string | SerializedStyles;
secondary: string | SerializedStyles;
disabled: string | SerializedStyles;
}

export interface Typography {
Expand Down Expand Up @@ -43,29 +43,29 @@ export interface Typography {
}

export interface ColoredTypography {
h1: (fontColor: string) => SerializedStyles;
h2: (fontColor: string) => SerializedStyles;
h3: (fontColor: string) => SerializedStyles;
h4: (fontColor: string) => SerializedStyles;
h5: (fontColor: string) => SerializedStyles;
s1: (fontColor: string) => SerializedStyles;
s2: (fontColor: string) => SerializedStyles;
b1: (fontColor: string) => SerializedStyles;
b2: (fontColor: string) => SerializedStyles;
b3: (fontColor: string) => SerializedStyles;
b4: (fontColor: string) => SerializedStyles;
c1: (fontColor: string) => SerializedStyles;
c2: (fontColor: string) => SerializedStyles;
c3: (fontColor: string) => SerializedStyles;
label: (fontColor: string) => SerializedStyles;
ButtonFont: (fontColor: string) => SerializedStyles;
Typeface: (fontColor: string) => SerializedStyles;
Giant: (fontColor: string) => SerializedStyles;
Large: (fontColor: string) => SerializedStyles;
Medium: (fontColor: string) => SerializedStyles;
small: (fontColor: string) => SerializedStyles;
Tiny: (fontColor: string) => SerializedStyles;
tag: (fontColor: string) => SerializedStyles;
h1: (fontColor: string | SerializedStyles) => SerializedStyles;
h2: (fontColor: string | SerializedStyles) => SerializedStyles;
h3: (fontColor: string | SerializedStyles) => SerializedStyles;
h4: (fontColor: string | SerializedStyles) => SerializedStyles;
h5: (fontColor: string | SerializedStyles) => SerializedStyles;
s1: (fontColor: string | SerializedStyles) => SerializedStyles;
s2: (fontColor: string | SerializedStyles) => SerializedStyles;
b1: (fontColor: string | SerializedStyles) => SerializedStyles;
b2: (fontColor: string | SerializedStyles) => SerializedStyles;
b3: (fontColor: string | SerializedStyles) => SerializedStyles;
b4: (fontColor: string | SerializedStyles) => SerializedStyles;
c1: (fontColor: string | SerializedStyles) => SerializedStyles;
c2: (fontColor: string | SerializedStyles) => SerializedStyles;
c3: (fontColor: string | SerializedStyles) => SerializedStyles;
label: (fontColor: string | SerializedStyles) => SerializedStyles;
ButtonFont: (fontColor: string | SerializedStyles) => SerializedStyles;
Typeface: (fontColor: string | SerializedStyles) => SerializedStyles;
Giant: (fontColor: string | SerializedStyles) => SerializedStyles;
Large: (fontColor: string | SerializedStyles) => SerializedStyles;
Medium: (fontColor: string | SerializedStyles) => SerializedStyles;
small: (fontColor: string | SerializedStyles) => SerializedStyles;
Tiny: (fontColor: string | SerializedStyles) => SerializedStyles;
tag: (fontColor: string | SerializedStyles) => SerializedStyles;
}
export interface Layout {
default: SerializedStyles;
Expand All @@ -76,4 +76,5 @@ export interface Theme {
typography: Typography;
semantic: Semantic;
layout: Layout;
coloredTypography: ColoredTypography;
}
2 changes: 2 additions & 0 deletions frontend/src/components/Chat/Chat.stories.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,8 @@ export const Default: Story = {
nickname: '테바',
date: '2024-08-01',
time: '12:12',
isConfirmChat: true,
chatType: 'BASIC',
isMyMessage: false,
},
},
Expand Down
14 changes: 7 additions & 7 deletions frontend/src/components/Chat/Chat.tsx
Original file line number Diff line number Diff line change
@@ -1,31 +1,31 @@
import {
chatMessageStyle,
messageContainer,
messageStyle,
senderStyle,
timeStyle,
} from './Chatstyle';

import type { Chat } from '@_types/index';
import { Chat as ChatType } from '@_types/index';
import { PropsWithChildren } from 'react';
import UserPreview from '@_components/UserPreview/UserPreview';
import { formatHhmmToKoreanWithPrefix } from '@_utils/formatters';
import { useTheme } from '@emotion/react';

export interface ChatMessageProps {
chat: Chat;
export interface ChatMessageProps extends PropsWithChildren {
chat: ChatType;
}

export default function Chat(props: ChatMessageProps) {
const { chat } = props;
const { content, nickname, time, isMyMessage } = chat;
const { chat, children } = props;
const { nickname, time, isMyMessage } = chat;
const theme = useTheme();
return (
<div css={chatMessageStyle({ isMyMessage })}>
{/* TODO: 추후 프로필 사진 추가시 변경해야함 */}
<UserPreview imageUrl={''} />
<div css={messageContainer({ isMyMessage })}>
<span css={senderStyle({ theme })}>{nickname}</span>
<div css={messageStyle({ theme, isMyMessage })}>{content}</div>
{children}
<span css={timeStyle({ theme })}>
{formatHhmmToKoreanWithPrefix(time)}
</span>
Expand Down
22 changes: 0 additions & 22 deletions frontend/src/components/Chat/Chatstyle.ts
Original file line number Diff line number Diff line change
Expand Up @@ -25,28 +25,6 @@ export const senderStyle = ({ theme }: { theme: Theme }) => css`
color:${theme.colorPalette.grey[900]};
`;

export const messageStyle = ({
theme,
isMyMessage,
}: {
theme: Theme;
isMyMessage: boolean;
}) => css`
${theme.typography.b4};
display: inline-block;
max-width: 25rem;
padding: 10px;
word-break: break-all;
background-color: ${isMyMessage
? theme.colorPalette.yellow[200]
: theme.colorPalette.orange[100]};
border-radius: ${isMyMessage ? '12px' : 0} ${isMyMessage ? 0 : '12px'} 12px
12px;
`;

export const timeStyle = ({ theme }: { theme: Theme }) => css`
${theme.typography.c3}
color:${theme.colorPalette.grey[400]};
Expand Down
63 changes: 63 additions & 0 deletions frontend/src/components/ChatBottomMenu/ChatBottomMenu.stories.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,63 @@
import type { Meta, StoryObj } from '@storybook/react';

import ChatBottomMenu from './ChatBottomMenu';
import ChatMenuItem from '@_components/ChatMenuItem/ChatMenuItem';
import { Fragment } from 'react';
import Plus from '@_common/assets/plus.svg';
import { css } from '@emotion/react';

const meta: Meta<typeof ChatBottomMenu> = {
component: ChatBottomMenu,
};

export default meta;
type Story = StoryObj<typeof ChatBottomMenu>;
export const Two: Story = {
args: {
children: (
<Fragment>
<ChatMenuItem icon={<Plus />} description="더하기" />
<ChatMenuItem icon={<Plus />} description="더하기" />
</Fragment>
),
},
decorators: (Story) => (
<div
css={css`
width: 300px;
`}
>
<Story />
</div>
),
};

export const Nine: Story = {
args: {
children: (
<Fragment>
<ChatMenuItem icon={<Plus />} description="더하기" />
<ChatMenuItem icon={<Plus />} description="더하기" />
<ChatMenuItem icon={<Plus />} description="더하기" />
<ChatMenuItem icon={<Plus />} description="더하기" />
<ChatMenuItem icon={<Plus />} description="더하기" />
<ChatMenuItem
icon={<Plus />}
description="더하기rrrrrrrrrrrrrrrrrrrrrrrrrrrrrr"
/>
<ChatMenuItem icon={<Plus />} description="더하기" />
<ChatMenuItem icon={<Plus />} description="더하기" />
<ChatMenuItem icon={<Plus />} description="더하기" />
</Fragment>
),
},
decorators: (Story) => (
<div
css={css`
width: 300px;
`}
>
<Story />
</div>
),
};
13 changes: 13 additions & 0 deletions frontend/src/components/ChatBottomMenu/ChatBottomMenu.style.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
import { Theme, css } from '@emotion/react';

export const menu = ({ theme }: { theme: Theme }) => css`
display: flex;
flex-wrap: wrap;
gap: 2rem;
width: 100%;
min-height: 40vh;
padding: 2rem;
background-color: ${theme.colorPalette.white[100]};
`;
11 changes: 11 additions & 0 deletions frontend/src/components/ChatBottomMenu/ChatBottomMenu.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
import * as S from './ChatBottomMenu.style';

import { PropsWithChildren } from 'react';
import { useTheme } from '@emotion/react';

export default function ChatBottomMenu(props: PropsWithChildren) {
const { children } = props;
const theme = useTheme();

return <div css={S.menu({ theme })}>{children}</div>;
}
14 changes: 14 additions & 0 deletions frontend/src/components/ChatBubble/ChatBubble.stories.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
import type { Meta, StoryObj } from '@storybook/react';

import ChatBubble from './ChatBubble';

const meta: Meta<typeof ChatBubble> = {
component: ChatBubble,
};

export default meta;
type Story = StoryObj<typeof ChatBubble>;

export const Default: Story = {
args: { isMyMessage: true, children: '안녕' },
};
23 changes: 23 additions & 0 deletions frontend/src/components/ChatBubble/ChatBubble.style.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,23 @@
import { Theme, css } from '@emotion/react';

export const chatBubble = ({
theme,
backgroundColor,
isMyMessage,
}: {
theme: Theme;
isMyMessage: boolean;
backgroundColor?: string;
}) => css`
${theme.typography.b4};
display: inline-block;
max-width: 25rem;
padding: 10px;
word-break: break-all;
background-color: ${backgroundColor || theme.semantic.primary};
border-radius: ${isMyMessage ? '12px' : 0} ${isMyMessage ? 0 : '12px'} 12px
12px;
`;
20 changes: 20 additions & 0 deletions frontend/src/components/ChatBubble/ChatBubble.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,20 @@
import * as S from './ChatBubble.style';

import { PropsWithChildren } from 'react';
import { useTheme } from '@emotion/react';

interface ChatBubbleProps extends PropsWithChildren {
isMyMessage: boolean;
backgroundColor?: string;
}

export default function ChatBubble(props: ChatBubbleProps) {
const { isMyMessage, backgroundColor, children } = props;
const theme = useTheme();

return (
<div css={S.chatBubble({ theme, backgroundColor, isMyMessage })}>
{children}
</div>
);
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
import { Theme, css } from '@emotion/react';

export const grey400C2 = ({ theme }: { theme: Theme }) => css`
${theme.typography.c2}
color:${theme.colorPalette.grey[400]}
`;
60 changes: 60 additions & 0 deletions frontend/src/components/ChatBubble/ChatChildren/ChatChildren.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,60 @@
import * as S from './ChatChildren.style';

import {
formatHhmmToKoreanWithPrefix,
formatYyyymmddToKorean,
} from '@_utils/formatters';

import { Chat } from '@_types/index';
import ChatBubble from '@_components/ChatBubble/ChatBubble';
import { useTheme } from '@emotion/react';

interface ChatChildrenProps {
chat: Chat;
}
export const ChatChildren = (props: ChatChildrenProps) => {
const { chat } = props;
const theme = useTheme();
if (chat.chatType === 'PLACE') {
return (
<ChatBubble
isMyMessage={chat.isMyMessage}
backgroundColor={theme.colorPalette.green[50]}
>
<div css={S.grey400C2({ theme })}>{'장소가 확정되었습니다!'}</div>
<div css={theme.typography.b3}>{chat.content}</div>
</ChatBubble>
);
}
if (chat.chatType === 'DATETIME') {
const [yyyymmdd, hhmm] = chat.content.split(' ');
return (
<ChatBubble
isMyMessage={chat.isMyMessage}
backgroundColor={theme.colorPalette.green[50]}
>
<div css={S.grey400C2({ theme })}>
{'날짜와 시간이 확정되었습니다!'}
</div>
<div css={theme.typography.b3}>
{formatYyyymmddToKorean(yyyymmdd, '-')}
</div>
<div css={theme.typography.b3}>
{formatHhmmToKoreanWithPrefix(hhmm, ':')}
</div>
</ChatBubble>
);
}
return (
<ChatBubble
isMyMessage={chat.isMyMessage}
backgroundColor={
chat.isMyMessage
? theme.colorPalette.yellow[200]
: theme.colorPalette.orange[100]
}
>
{chat.content}
</ChatBubble>
);
};
Loading

0 comments on commit 4c15bec

Please sign in to comment.