Skip to content

Commit d17283f

Browse files
authored
[#48] ✨ 공통 컴포넌트 Container 개발 (#87)
* [#20] 💄 add dark gray color to gray scale * [#48] ✨ add Container component * [#48] ✨ add Box component * [#48] ✨ add grid component and index.tsx as a bridge * [#48] ✅ add storybooks * [#48] 🔧 change the path of grid stories
1 parent 81371ef commit d17283f

File tree

7 files changed

+335
-0
lines changed

7 files changed

+335
-0
lines changed
Lines changed: 78 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,78 @@
1+
import { twMerge } from 'tailwind-merge'
2+
3+
type Variant = 'contained' | 'outlined'
4+
type Rounded = 8 | 12
5+
type Padding = 0 | 10 | 20 | 30 | 40
6+
type Margin = 0 | 10 | 20 | 30 | 40
7+
type Color = 'primary' | 'secondary' | 'tertiary'
8+
9+
interface BoxProps {
10+
children: React.ReactNode
11+
variant?: Variant
12+
rounded?: Rounded
13+
padding?: Padding
14+
margin?: Margin
15+
color?: Color
16+
className?: string
17+
}
18+
19+
const BaseStyle = 'flex items-center justify-center w-full'
20+
21+
const styleByVariant: Record<Variant, string> = {
22+
outlined: 'border-1 border-solid border-gray-200 bg-common-white',
23+
contained: 'bg-gray-100',
24+
}
25+
26+
const styleByRounded: Record<Rounded, string> = {
27+
8: 'rounded-8',
28+
12: 'rounded-12',
29+
}
30+
31+
const styleByPadding: Record<Padding, string> = {
32+
0: 'p-0',
33+
10: 'p-10',
34+
20: 'p-20',
35+
30: 'p-30',
36+
40: 'p-40',
37+
}
38+
39+
const styleByMargin: Record<Margin, string> = {
40+
0: 'm-0',
41+
10: 'm-10',
42+
20: 'm-20',
43+
30: 'm-30',
44+
40: 'm-40',
45+
}
46+
47+
const styleByColor: Record<Color, string> = {
48+
primary: 'bg-common-white',
49+
secondary: 'bg-gray-100',
50+
tertiary: 'bg-gray-900',
51+
}
52+
53+
export const Box = ({
54+
children,
55+
variant = 'outlined',
56+
rounded = 12,
57+
padding = 20,
58+
margin = 10,
59+
color = 'primary',
60+
className = '',
61+
}: BoxProps): JSX.Element => {
62+
const roundedStyle = styleByRounded[rounded] || ''
63+
const paddingStyle = styleByPadding[padding] || ''
64+
const marginStyle = styleByMargin[margin] || ''
65+
const colorStyle = styleByColor[color] || ''
66+
67+
const boxStyle = twMerge(
68+
BaseStyle,
69+
styleByVariant[variant],
70+
roundedStyle,
71+
paddingStyle,
72+
marginStyle,
73+
colorStyle,
74+
className
75+
)
76+
77+
return <div className={boxStyle}>{children}</div>
78+
}
Lines changed: 25 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,25 @@
1+
import { twMerge } from 'tailwind-merge'
2+
3+
type ContainerProps<T extends React.ElementType> = {
4+
as?: T
5+
children: React.ReactNode
6+
className?: string
7+
} & React.ComponentPropsWithoutRef<T>
8+
9+
export const Container = <T extends React.ElementType = 'div'>({
10+
as,
11+
children,
12+
className,
13+
...props
14+
}: ContainerProps<T>): JSX.Element => {
15+
const Component = as || 'div'
16+
17+
const baseStyle = 'mx-auto max-w-1200'
18+
const containerStyle = twMerge(baseStyle, className)
19+
20+
return (
21+
<Component className={containerStyle} {...props}>
22+
{children}
23+
</Component>
24+
)
25+
}
Lines changed: 103 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,103 @@
1+
import clsx from 'clsx'
2+
import { twMerge } from 'tailwind-merge'
3+
4+
type Column = 1 | 2 | 3 | 4 | 6 | 12
5+
type Spacing = 0 | 4 | 8 | 12 | 16 | 20
6+
7+
interface GridProps {
8+
children: React.ReactNode
9+
className?: string
10+
}
11+
12+
interface GridContainerProps extends GridProps {
13+
columns?: Column // 열 개수
14+
spacing?: Spacing // 아이템 간의 간격
15+
rowGap?: Spacing // 행 간 간격
16+
}
17+
18+
interface GridItemProps extends GridProps {
19+
colSpan?: Column // 아이템이 차지할 열의 개수
20+
}
21+
22+
const baseStyle = 'grid'
23+
24+
const styleByColumns: Record<Column, string> = {
25+
1: 'grid-cols-1',
26+
2: 'grid-cols-2',
27+
3: 'grid-cols-3',
28+
4: 'grid-cols-4',
29+
6: 'grid-cols-6',
30+
12: 'grid-cols-12',
31+
}
32+
33+
const styleBySpacing: Record<Spacing, string> = {
34+
0: 'gap-0',
35+
4: 'gap-4',
36+
8: 'gap-8',
37+
12: 'gap-12',
38+
16: 'gap-16',
39+
20: 'gap-20',
40+
}
41+
42+
const styleByRowGap: Record<Spacing, string> = {
43+
0: 'gap-y-0',
44+
4: 'gap-y-4',
45+
8: 'gap-y-8',
46+
12: 'gap-y-12',
47+
16: 'gap-y-16',
48+
20: 'gap-y-20',
49+
}
50+
51+
const styleByColSpan: Record<Column, string> = {
52+
1: 'col-span-1',
53+
2: 'col-span-2',
54+
3: 'col-span-3',
55+
4: 'col-span-4',
56+
6: 'col-span-6',
57+
12: 'col-span-12',
58+
}
59+
60+
const GridContainer = ({
61+
children,
62+
className = '',
63+
columns = 12,
64+
spacing = 0,
65+
rowGap = 0,
66+
}: GridContainerProps): JSX.Element => {
67+
const columnClass = styleByColumns[columns] || ''
68+
const spacingClass = styleBySpacing[spacing] || ''
69+
const rowGapClass = styleByRowGap[rowGap] || ''
70+
71+
const containerStyle = twMerge(
72+
baseStyle,
73+
columnClass,
74+
spacingClass,
75+
rowGapClass,
76+
className
77+
)
78+
79+
return <div className={containerStyle}>{children}</div>
80+
}
81+
82+
const GridItem = ({
83+
children,
84+
className = '',
85+
colSpan = 1,
86+
}: GridItemProps): JSX.Element => {
87+
const colSpanClass = styleByColSpan[colSpan] || ''
88+
const itemStyle = clsx(colSpanClass, className)
89+
90+
return <div className={itemStyle}>{children}</div>
91+
}
92+
93+
interface GridCompoundComponent extends React.Component<GridProps> {
94+
Container: typeof GridContainer
95+
Item: typeof GridItem
96+
}
97+
98+
const Grid = {} as GridCompoundComponent
99+
100+
Grid.Container = GridContainer
101+
Grid.Item = GridItem
102+
103+
export { Grid }
Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,5 @@
1+
import { Box } from '@/components/common/containers/Box'
2+
import { Container } from '@/components/common/containers/Container'
3+
import { Grid } from '@/components/common/containers/Grid'
4+
5+
export { Box, Container, Grid }
Lines changed: 75 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,75 @@
1+
import { Meta, StoryObj } from '@storybook/react'
2+
3+
import { Box } from '@/components/common/containers'
4+
5+
const meta: Meta<typeof Box> = {
6+
title: 'Common/Containers/Box',
7+
component: Box,
8+
argTypes: {
9+
variant: {
10+
control: 'select',
11+
options: ['contained', 'outlined'],
12+
description: 'Box variant type',
13+
},
14+
rounded: {
15+
control: 'radio',
16+
options: [8, 12],
17+
description: 'Box rounded corners',
18+
},
19+
padding: {
20+
control: 'select',
21+
options: [0, 10, 20, 30, 40],
22+
description: 'Padding inside the box',
23+
},
24+
margin: {
25+
control: 'select',
26+
options: [0, 10, 20, 30, 40],
27+
description: 'Margin outside the box',
28+
},
29+
color: {
30+
control: 'select',
31+
options: ['primary', 'secondary', 'tertiary'],
32+
description: 'Background color of the box',
33+
},
34+
className: {
35+
control: 'text',
36+
description: 'Custom additional classes',
37+
},
38+
},
39+
}
40+
41+
export default meta
42+
43+
export const Default: StoryObj = {
44+
args: {
45+
children: 'This is a Box component',
46+
variant: 'outlined',
47+
rounded: 12,
48+
padding: 20,
49+
margin: 10,
50+
color: 'primary',
51+
},
52+
}
53+
54+
export const ContainedBox: StoryObj = {
55+
args: {
56+
children: 'This is a contained Box component',
57+
variant: 'contained',
58+
rounded: 8,
59+
padding: 30,
60+
margin: 20,
61+
color: 'secondary',
62+
},
63+
}
64+
65+
export const CustomizedBox: StoryObj = {
66+
args: {
67+
children: 'Customized Box with extra styles',
68+
variant: 'outlined',
69+
rounded: 12,
70+
padding: 40,
71+
margin: 10,
72+
color: 'tertiary',
73+
className: 'shadow-lg',
74+
},
75+
}
Lines changed: 48 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,48 @@
1+
import { Meta, StoryObj } from '@storybook/react'
2+
3+
import { Grid } from '@/components/common/containers'
4+
5+
export default {
6+
title: 'Common/Containers/Grid',
7+
component: Grid.Container,
8+
subcomponents: { GridItem: Grid.Item },
9+
parameters: {
10+
layout: 'centered',
11+
},
12+
} as Meta
13+
14+
export const Default: StoryObj = {
15+
render: () => (
16+
<Grid.Container columns={3} spacing={16}>
17+
<Grid.Item>Item 1</Grid.Item>
18+
<Grid.Item>Item 2</Grid.Item>
19+
<Grid.Item>Item 3</Grid.Item>
20+
<Grid.Item>Item 4</Grid.Item>
21+
</Grid.Container>
22+
),
23+
}
24+
25+
export const CustomSpacing: StoryObj = {
26+
render: () => (
27+
<Grid.Container columns={3} spacing={8} rowGap={12}>
28+
<Grid.Item colSpan={1}>Item 1</Grid.Item>
29+
<Grid.Item colSpan={2}>Item 2</Grid.Item>
30+
<Grid.Item colSpan={2}>Item 3</Grid.Item>
31+
<Grid.Item colSpan={1}>Item 4</Grid.Item>
32+
<Grid.Item colSpan={4}>Item 5</Grid.Item>
33+
</Grid.Container>
34+
),
35+
}
36+
37+
export const CustomClasses: StoryObj = {
38+
render: () => (
39+
<Grid.Container columns={2} spacing={16} className='custom-container'>
40+
<Grid.Item colSpan={1} className='custom-item'>
41+
Custom Styled Item
42+
</Grid.Item>
43+
<Grid.Item colSpan={1} className='custom-item'>
44+
Custom Styled Item
45+
</Grid.Item>
46+
</Grid.Container>
47+
),
48+
}

tailwind.config.ts

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -28,6 +28,7 @@ const colorPalette: Record<
2828
600: '#808080',
2929
700: '#555',
3030
800: '#333',
31+
900: '#232323',
3132
},
3233
red: {
3334
100: '#ffebe7',

0 commit comments

Comments
 (0)