-
Notifications
You must be signed in to change notification settings - Fork 3
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Signed-off-by: Lukas J Han <[email protected]> Signed-off-by: Lukas.J Han <[email protected]>
- Loading branch information
Showing
3 changed files
with
269 additions
and
0 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,115 @@ | ||
import React from 'react'; | ||
import { Label } from './Label'; | ||
import { Color } from '../colors/color.type'; | ||
|
||
export type ButtonProps<E extends React.ElementType> = { | ||
variant?: 'primary' | 'secondary' | 'tertiary' | 'text'; | ||
size?: 'x-small' | 'small' | 'medium' | 'large' | 'x-large'; | ||
children: React.ReactNode; | ||
className?: string; | ||
disabled?: boolean; | ||
} & React.ComponentPropsWithoutRef<E>; | ||
|
||
export const Button = <E extends React.ElementType = 'button'>({ | ||
variant = 'primary', | ||
size = 'large', | ||
children, | ||
className = '', | ||
disabled = false, | ||
...props | ||
}: ButtonProps<E>) => { | ||
const baseStyles = | ||
'inline-flex items-center justify-center rounded-4 font-medium focus:outline-none focus:ring-2 focus:ring-offset-2 transition-colors duration-200'; | ||
|
||
const variantStyles: { | ||
style: string; | ||
color: Color; | ||
disabledStyle: string; | ||
disabledColor: Color; | ||
} = { | ||
primary: { | ||
style: 'bg-primary hover:bg-primary-60', | ||
color: 'gray-0' as Color, | ||
disabledStyle: 'bg-primary-20', | ||
disabledColor: 'gray-0' as Color, | ||
}, | ||
secondary: { | ||
style: 'bg-gray-0 hover:bg-primary-5 border border-primary', | ||
color: 'primary' as Color, | ||
disabledStyle: 'bg-gray-0 border border-primary-30', | ||
disabledColor: 'primary-30' as Color, | ||
}, | ||
tertiary: { | ||
style: 'bg-gray-0 hover:bg-gray-5 border border-gray-90', | ||
color: 'gray-90' as Color, | ||
disabledStyle: 'bg-gray-0 border border-gray-40', | ||
disabledColor: 'gray-40' as Color, | ||
}, | ||
text: { | ||
style: 'bg-transparent', | ||
color: 'gray-90' as Color, | ||
disabledStyle: 'bg-transparent', | ||
disabledColor: 'gray-40' as Color, | ||
}, | ||
}[variant]; | ||
|
||
const sizeStyles: { style: string; fontSize: 'l' | 'm' | 's' | 'xs' } = { | ||
'x-small': { | ||
style: 'px-3 min-w-[32px] min-h-[32px]', | ||
fontSize: 's' as const, | ||
}, | ||
small: { | ||
style: 'px-3 min-w-[40px] min-h-[40px]', | ||
fontSize: 'm' as const, | ||
}, | ||
medium: { | ||
style: 'px-4 min-w-[48px] min-h-[48px]', | ||
fontSize: 'm' as const, | ||
}, | ||
large: { | ||
style: 'px-6 min-w-[56px] min-h-[56px]', | ||
fontSize: 'l' as const, | ||
}, | ||
'x-large': { | ||
style: 'px-8 min-w-[64px] min-h-[64px]', | ||
fontSize: 'l' as const, | ||
}, | ||
}[size]; | ||
|
||
const buttonStyles = ` | ||
${baseStyles} | ||
${disabled ? variantStyles.disabledStyle : variantStyles.style} | ||
${sizeStyles.style} | ||
${disabled ? 'cursor-not-allowed' : ''} | ||
${className} | ||
`; | ||
|
||
const handleKeyDown = (event: React.KeyboardEvent<HTMLButtonElement>) => { | ||
if (event.key === 'Enter' || event.key === ' ') { | ||
event.preventDefault(); | ||
if (!disabled) { | ||
(event.currentTarget as HTMLButtonElement).click(); | ||
} | ||
} | ||
}; | ||
|
||
const labelStyles = `${disabled ? 'cursor-not-allowed' : 'cursor-pointer'} ${variant === 'text' ? 'underline' : ''}`; | ||
|
||
return ( | ||
<button | ||
className={buttonStyles} | ||
onKeyDown={handleKeyDown} | ||
role="button" | ||
disabled={disabled} | ||
{...props} | ||
> | ||
<Label | ||
color={disabled ? variantStyles.disabledColor : variantStyles.color} | ||
size={sizeStyles.fontSize} | ||
className={labelStyles} | ||
> | ||
{children} | ||
</Label> | ||
</button> | ||
); | ||
}; |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,151 @@ | ||
import type { Meta, StoryObj } from '@storybook/react'; | ||
import { fn } from '@storybook/test'; | ||
import { Button } from '../../packages/core/lib'; | ||
|
||
const meta = { | ||
title: 'Components/Button', | ||
component: Button, | ||
parameters: { | ||
layout: 'centered', | ||
}, | ||
tags: ['autodocs'], | ||
argTypes: { | ||
variant: { | ||
control: { | ||
type: 'select', | ||
options: ['primary', 'secondary', 'tertiary'], | ||
}, | ||
}, | ||
size: { | ||
control: { | ||
type: 'select', | ||
options: ['x-small', 'small', 'medium', 'large', 'x-large'], | ||
}, | ||
}, | ||
disabled: { | ||
control: 'boolean', | ||
}, | ||
onClick: { action: 'clicked' }, | ||
}, | ||
} satisfies Meta<typeof Button>; | ||
|
||
export default meta; | ||
type Story = StoryObj<typeof meta>; | ||
|
||
export const Primary: Story = { | ||
args: { | ||
variant: 'primary', | ||
children: '버튼: Primary', | ||
onClick: fn(), | ||
}, | ||
}; | ||
|
||
export const Secondary: Story = { | ||
args: { | ||
variant: 'secondary', | ||
children: '버튼: Secondary', | ||
onClick: fn(), | ||
}, | ||
}; | ||
|
||
export const Tertiary: Story = { | ||
args: { | ||
variant: 'tertiary', | ||
children: '버튼: Tertiary', | ||
onClick: fn(), | ||
}, | ||
}; | ||
|
||
export const Text: Story = { | ||
args: { | ||
variant: 'text', | ||
children: '버튼: Text', | ||
size: 'small', | ||
onClick: fn(), | ||
}, | ||
}; | ||
|
||
export const XSmall: Story = { | ||
args: { | ||
variant: 'primary', | ||
children: 'X-Small 버튼', | ||
size: 'x-small', | ||
onClick: fn(), | ||
}, | ||
}; | ||
|
||
export const Small: Story = { | ||
args: { | ||
variant: 'primary', | ||
children: 'Small 버튼', | ||
size: 'small', | ||
onClick: fn(), | ||
}, | ||
}; | ||
|
||
export const Medium: Story = { | ||
args: { | ||
variant: 'primary', | ||
children: 'Medium 버튼', | ||
size: 'medium', | ||
onClick: fn(), | ||
}, | ||
}; | ||
|
||
export const Large: Story = { | ||
args: { | ||
variant: 'primary', | ||
children: 'Large 버튼', | ||
size: 'large', | ||
onClick: fn(), | ||
}, | ||
}; | ||
|
||
export const XLarge: Story = { | ||
args: { | ||
variant: 'primary', | ||
children: 'X-Large 버튼', | ||
size: 'x-large', | ||
onClick: fn(), | ||
}, | ||
}; | ||
|
||
export const Disabled: Story = { | ||
args: { | ||
variant: 'primary', | ||
children: 'Disabled Button', | ||
size: 'medium', | ||
disabled: true, | ||
onClick: fn(), | ||
}, | ||
}; | ||
|
||
export const DisabledSecondary: Story = { | ||
args: { | ||
variant: 'secondary', | ||
children: 'Disabled Button', | ||
size: 'medium', | ||
disabled: true, | ||
onClick: fn(), | ||
}, | ||
}; | ||
|
||
export const DisabledTertiary: Story = { | ||
args: { | ||
variant: 'tertiary', | ||
children: 'Disabled Button', | ||
size: 'medium', | ||
disabled: true, | ||
onClick: fn(), | ||
}, | ||
}; | ||
|
||
export const DisabledText: Story = { | ||
args: { | ||
variant: 'text', | ||
children: 'Disabled Button', | ||
size: 'small', | ||
disabled: true, | ||
onClick: fn(), | ||
}, | ||
}; |