Skip to content

Commit

Permalink
Merge branch 'develop' into feature/177-textfield
Browse files Browse the repository at this point in the history
  • Loading branch information
guesung authored Aug 12, 2023
2 parents 5daa770 + 890d852 commit f42fe1d
Show file tree
Hide file tree
Showing 10 changed files with 196 additions and 3 deletions.
52 changes: 52 additions & 0 deletions src/components/Button/Button.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,52 @@
import cn from '@/utils/cn';

export interface ButtonProps extends React.ButtonHTMLAttributes<HTMLButtonElement> {
/**
* 버튼의 크기를 설정합니다. small: 48px, medium: 56px (default: medium)
*/
size?: 'small' | 'medium';
/**
* 버튼의 색상을 설정합니다. (default: solid-primary)
*/
variant?:
| 'solid-primary'
| 'solid-default'
| 'solid-secondary'
| 'outline-warning'
| 'solid-warning';
children: React.ReactNode;
}

export default function Button({
size = 'medium',
variant = 'solid-primary',
className,
disabled,
children,
...props
}: ButtonProps) {
return (
<button
className={cn(
'flex items-center justify-center rounded-8 px-24 py-16 text-subtitle-2',
{
'h-56': size === 'medium',
'h-48': size === 'small',
'bg-primary text-sign-white disabled:bg-primary-light': variant === 'solid-primary',
'bg-button text-sign-secondary disabled:bg-sub disabled:text-sign-caption':
variant === 'solid-default',
'bg-brand-color text-sign-brand disabled:text-sign-white': variant === 'solid-secondary',
'border border-warning bg-warning-color text-warning disabled:border-warning-light disabled:bg-white disabled:text-warning-light':
variant === 'outline-warning',
'bg-warning text-sign-white disabled:bg-sub disabled:text-sign-caption':
variant === 'solid-warning',
},
className
)}
disabled={disabled}
{...props}
>
{children}
</button>
);
}
59 changes: 59 additions & 0 deletions src/components/Button/ButtonGroup.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,59 @@
import cn from '@/utils/cn';
import { Children, type ReactElement, cloneElement, isValidElement } from 'react';

import type { ButtonProps } from './Button';

interface ButtonGroupProps {
/**
* 버튼 그룹의 위치를 설정합니다. (default: bottom)
*/
position?: 'bottom' | 'contents';
children: React.ReactNode;
}

export default function ButtonGroup({ position = 'bottom', children }: ButtonGroupProps) {
const validChildren = Children.toArray(children).filter(
(child) =>
isValidElement(child) &&
(
child.type as {
name: string;
}
).name === 'Button'
) as ReactElement[];

const renderElements = (elements: ReactElement[]) => {
if (elements.length === 1) {
const props = elements[0].props as ButtonProps;
return cloneElement(elements[0], {
className: cn('w-full', props.className),
});
}

return (
<div className="flex gap-8">
{elements.map((element, index) => {
const props = elements[index].props as ButtonProps;

if (index === 0) {
return cloneElement(element, {
className: cn('flex-shrink-0', props.className),
variant: props.variant ?? 'solid-default',
});
}
return cloneElement(element, { className: cn('w-full', props.className) });
})}
</div>
);
};

return (
<div
className={cn('mx-auto border-t-1 border-divider bg-white p-20 pt-7', {
'fixed inset-x-0 bottom-0 max-w-450': position === 'bottom',
})}
>
{renderElements(validChildren)}
</div>
);
}
32 changes: 32 additions & 0 deletions src/components/Button/FloatButton.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,32 @@
import cn from '@/utils/cn';
import Image from 'next/image';

interface FloatButtonProps {
/**
* FloatButton의 className을 설정합니다.
*/
className?: string;
/**
* FloatButton의 클릭 이벤트를 설정합니다.
*/
onClick?: () => void;
/**
* FloatButton의 비활성화 여부를 설정합니다.
*/
disabled?: boolean;
}

export default function FloatButton({ className, onClick, disabled }: FloatButtonProps) {
return (
<button
className={cn(
'flex h-60 w-60 items-center justify-center rounded-full bg-primary text-sign-white shadow-float active:bg-primary-dark disabled:bg-primary-light',
className
)}
onClick={onClick}
disabled={disabled}
>
<Image src="/icons/24/add.svg" width={24} height={24} alt="add" />
</button>
);
}
27 changes: 27 additions & 0 deletions src/components/Button/IconButton.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,27 @@
import cn from '@/utils/cn';

interface IconButtonProps {
/**
* 버튼의 크기를 설정합니다. small: 24px, medium: 40px, large: 48px (default: small)
*/
size?: 'small' | 'medium' | 'large';
/**
* 버튼의 클릭 이벤트를 설정합니다.
*/
onClick?: () => void;
children: React.ReactNode;
}
export default function IconButton({ onClick, children, size = 'small' }: IconButtonProps) {
return (
<button
className={cn('flex items-center justify-center', {
'h-24 w-24': size === 'small',
'h-40 w-40': size === 'medium',
'h-48 w-48': size === 'large',
})}
onClick={onClick}
>
{children}
</button>
);
}
4 changes: 4 additions & 0 deletions src/components/Button/index.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
export { default as Button } from './Button';
export { default as ButtonGroup } from './ButtonGroup';
export { default as FloatButton } from './FloatButton';
export { default as IconButton } from './IconButton';
5 changes: 5 additions & 0 deletions src/style/theme/boxShadow.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
export const boxShadow = {
float: '4px 8px 28px 0px rgba(0, 0, 0, 0.08), 0px 4px 12px 0px rgba(0, 0, 0, 0.16)',
navigation: '0px 0px 4px 0px rgba(0, 0, 0, 0.10), 2px 4px 12px 0px rgba(0, 0, 0, 0.10)',
'card-ui': '2px 2px 10px 0px rgba(0, 0, 0, 0.04), 2px 2px 20px 0px rgba(0, 0, 0, 0.06)',
} as const;
1 change: 1 addition & 0 deletions src/style/theme/colors.ts
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@ export const colors = {
// Background Colors
white: PALETTE.white,
sub: PALETTE.gray['050'],
button: PALETTE.gray['200'],
divider: PALETTE.gray['100'],
'card-ui': PALETTE.gray['030'],
'brand-color': PALETTE.blue['050'],
Expand Down
1 change: 1 addition & 0 deletions src/style/theme/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2,3 +2,4 @@ export * from './colors';
export * from './animations';
export * from './spacing';
export * from './fontSizes';
export * from './boxShadow';
15 changes: 13 additions & 2 deletions src/utils/cn.ts
Original file line number Diff line number Diff line change
@@ -1,10 +1,21 @@
import { fontSizes } from '@/style/theme';
import { clsx } from 'clsx';
import { twMerge } from 'tailwind-merge';
import { extendTailwindMerge } from 'tailwind-merge';

import type { ClassValue } from 'clsx';

const customTwMerge = extendTailwindMerge({
classGroups: {
'font-size': [
{
text: Object.keys(fontSizes),
},
],
},
});

const cn = (...inputs: ClassValue[]) => {
return twMerge(clsx(inputs));
return customTwMerge(clsx(inputs));
};

export default cn;
3 changes: 2 additions & 1 deletion tailwind.config.js
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
const { colors, pxToRemTailwind, animations, fontSizes } = require('./src/style/theme');
const { colors, boxShadow, pxToRemTailwind, animations, fontSizes } = require('./src/style/theme');

/** @type {import('tailwindcss').Config} */
module.exports = {
Expand All @@ -7,6 +7,7 @@ module.exports = {
extend: {
...pxToRemTailwind,
colors,
boxShadow,
keyframes: animations,
animation: {
slideUp: 'slideUp 0.5s ease-in-out',
Expand Down

0 comments on commit f42fe1d

Please sign in to comment.