Skip to content

Commit

Permalink
Merge pull request #259 from woowacourse-teams/feat/#258
Browse files Browse the repository at this point in the history
버튼 컴포넌트와 emotion/theme을 유연화
  • Loading branch information
ss0526100 authored Aug 7, 2024
2 parents dbdcfd9 + c4cc703 commit 57bab1c
Show file tree
Hide file tree
Showing 8 changed files with 183 additions and 41 deletions.
20 changes: 20 additions & 0 deletions frontend/src/common/theme/coloredTypography.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,20 @@
import { ColoredTypography, Typography } from './theme.type';

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) => {
return css`
${style}
color:${fontColor};
`;
};

return object;
}, {} as ColoredTypography);

export default coloredTypography;
2 changes: 1 addition & 1 deletion frontend/src/common/theme/semantic.ts
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
import colorPalette from './colorPalette';

const semantic = {
primary: colorPalette.orange[400],
primary: colorPalette.orange[300],
secondary: colorPalette.orange[50],
disabled: colorPalette.grey[300],
};
Expand Down
25 changes: 25 additions & 0 deletions frontend/src/common/theme/theme.type.ts
Original file line number Diff line number Diff line change
Expand Up @@ -42,6 +42,31 @@ export interface Typography {
tag: SerializedStyles;
}

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;
}
export interface Layout {
default: SerializedStyles;
}
Expand Down
14 changes: 14 additions & 0 deletions frontend/src/components/Button/Button.stories.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
import type { Meta, StoryObj } from '@storybook/react';

import Button from './Button';

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

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

export const Default: Story = {
args: {},
};
122 changes: 94 additions & 28 deletions frontend/src/components/Button/Button.style.ts
Original file line number Diff line number Diff line change
@@ -1,40 +1,92 @@
import { css, Theme } from '@emotion/react';
import { Theme, css } from '@emotion/react';

const font = css`
import { ButtonProps } from './Button';

const defaultFont = css`
font-size: 1.6rem;
font-weight: 700;
font-style: normal;
line-height: normal;
color: #fff;
letter-spacing: -0.032rem;
`;
export const shapes = (
shape: 'circle' | 'bar',
disabled: boolean,
theme: Theme,
) => {
if (shape === 'circle') {

type themeStyleArgs = Pick<
ButtonProps,
'disabled' | 'primary' | 'secondary' | 'reversePrimary'
> & { theme: Theme };

const themeStyle = ({
theme,
disabled,
primary,
secondary,
reversePrimary,
}: themeStyleArgs) => {
if (disabled) {
return css`
${font};
flex-shrink: 0;
pointer-events: none;
color: ${theme.colorPalette.white[100]};
background-color: ${theme.semantic.disabled};
`;
}
if (primary) {
return css`
color: ${theme.colorPalette.white[100]};
background-color: ${theme.semantic.primary};
background: ${disabled
? theme.colorPalette.grey[300]
: theme.colorPalette.white[100]};
border: none;
border-radius: 50%;
box-shadow: 0 0 3px #444;
&:active {
background-color: ${theme.colorPalette.orange[900]};
}
`;
}
if (secondary) {
return css`
color: ${theme.colorPalette.white[100]};
background-color: ${theme.semantic.secondary};
&:active {
background-color: #868e96;
background-color: ${theme.colorPalette.yellow[300]};
}
`;
}

if (reversePrimary) {
return css`
color: ${theme.semantic.primary};
background-color: ${theme.colorPalette.white[100]};
border: solid 1px ${theme.semantic.primary};
&:active {
background-color: ${theme.colorPalette.grey[300]};
}
`;
}

// default:primary
return css`
color: ${theme.colorPalette.white};
background-color: ${theme.semantic.primary};
&:active {
background-color: ${theme.colorPalette.orange[500]};
}
`;
};

type shapeStyleArgs = Pick<ButtonProps, 'shape'>;

const shapeStyle = ({ shape }: shapeStyleArgs) => {
if (shape === 'circle') {
return css`
border: none;
border-radius: 50%;
box-shadow: 0 0 3px #444;
`;
}
if (shape === 'bar') {
return css`
${font}
display: flex;
flex-shrink: 0;
gap: 1rem;
align-items: center;
justify-content: center;
Expand All @@ -43,17 +95,31 @@ export const shapes = (
height: 6.4rem;
padding: 1.6rem 5.9rem;
background: ${disabled
? theme.colorPalette.grey[300]
: theme.colorPalette.orange[300]};
border: none;
border-radius: 3rem;
&:active {
background-color: ${disabled
? theme.colorPalette.grey[400]
: theme.colorPalette.orange[400]};
}
`;
}
};

type ShapeArgs = ButtonProps & { theme: Theme };
export const shapes = ({
theme,
shape,

disabled,
primary,
reversePrimary,

secondary,
}: ShapeArgs) => {
const defaultStyle = css`
user-select: none;
flex-shrink: 0;
`;
return [
defaultStyle,
defaultFont,
shapeStyle({ shape }),
themeStyle({ theme, disabled, primary, secondary, reversePrimary }),
];
};
22 changes: 17 additions & 5 deletions frontend/src/components/Button/Button.tsx
Original file line number Diff line number Diff line change
@@ -1,20 +1,32 @@
import {
Interpolation,
SerializedStyles,
Theme,
useTheme,
} from '@emotion/react';

import { ReactNode } from 'react';
import { shapes } from '@_components/Button/Button.style';
import { useTheme } from '@emotion/react';

interface ButtonProps {
export interface ButtonProps {
shape: 'circle' | 'bar';
onClick?: () => void;
disabled: boolean;
disabled?: boolean;
primary?: boolean;
secondary?: boolean;
reversePrimary?: boolean;
hasBorder?: boolean;

font?: SerializedStyles | Interpolation<Theme>;
children: ReactNode;
}

export default function Button(props: ButtonProps) {
const { shape, onClick, disabled, children } = props;
const { onClick, disabled, children, font } = props;
const theme = useTheme();
return (
<button
css={shapes(shape, disabled, theme)}
css={[shapes({ ...props, theme }), font]}
onClick={onClick}
disabled={disabled}
>
Expand Down
15 changes: 8 additions & 7 deletions frontend/src/pages/MainPage/MainPage.tsx
Original file line number Diff line number Diff line change
@@ -1,14 +1,14 @@
import { Fragment, useState } from 'react';
import MoimTabBar, { MainPageTab } from '@_components/MoimTabBar/MoimTabBar';

import Button from '@_components/Button/Button';
import HomeLayout from '@_layouts/HomeLayout.tsx/HomeLayout';
import HomeMainContent from '@_components/HomeMainContent/HomeMainContent';
import NavigationBar from '@_components/NavigationBar/NavigationBar';
import NavigationBarWrapper from '@_layouts/components/NavigationBarWrapper/NavigationBarWrapper';
import PlusIcon from '@_components/Icons/PlusIcon';
import ROUTES from '@_constants/routes';
import { useNavigate } from 'react-router-dom';
import PlusIcon from '@_components/Icons/PlusIcon';
import { Fragment, useState } from 'react';
import NavigationBarWrapper from '@_layouts/components/NavigationBarWrapper/NavigationBarWrapper';
import NavigationBar from '@_components/NavigationBar/NavigationBar';

import MoimTabBar, { MainPageTab } from '@_components/MoimTabBar/MoimTabBar';
import HomeMainContent from '@_components/HomeMainContent/HomeMainContent';

export default function MainPage() {
const navigate = useNavigate();
Expand Down Expand Up @@ -36,6 +36,7 @@ export default function MainPage() {
shape="circle"
onClick={() => navigate(ROUTES.addMoim)}
disabled={false}
reversePrimary
>
<PlusIcon />
</Button>
Expand Down
4 changes: 4 additions & 0 deletions frontend/src/types/index.d.ts
Original file line number Diff line number Diff line change
@@ -1,3 +1,7 @@
export type Entries<T> = {
[K in keyof T]: [K, T[K]];
}[keyof T][];

export interface MoimInfo {
moimId: number;
title: string;
Expand Down

0 comments on commit 57bab1c

Please sign in to comment.