-
Notifications
You must be signed in to change notification settings - Fork 0
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
* chore(packages/ui): ovarlay-kit 의존성 추가 * feat(packages/ui): Toast 컴포넌트 구현 * test(apps/web): Toast 컴포넌트 사용 예시 추가 * fix(packages/ui): 접근성 개선 * chore(packages/ui): lock 파일 업데이트 * chore(packages/themes): violet 색상 추가 * chore(packages/ui): success일 경우의 색상 변경 * fix(apps/web): Providers 컴포넌트 분리, OverlayProvider 이동
- Loading branch information
1 parent
49fe65f
commit 562478e
Showing
19 changed files
with
482 additions
and
12 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
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
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,17 @@ | ||
'use client'; | ||
|
||
import { ThemeProvider } from '@repo/theme'; | ||
import { OverlayProvider } from 'overlay-kit'; | ||
import { ReactNode } from 'react'; | ||
|
||
type ProvidersProps = { | ||
children: ReactNode; | ||
}; | ||
|
||
export function Providers({ children }: ProvidersProps) { | ||
return ( | ||
<ThemeProvider theme="light"> | ||
<OverlayProvider>{children}</OverlayProvider> | ||
</ThemeProvider> | ||
); | ||
} |
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
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
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
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,24 @@ | ||
import { style } from '@vanilla-extract/css'; | ||
import { tokens } from '@repo/theme'; | ||
|
||
export const container = style({ | ||
position: 'fixed', | ||
bottom: 40, | ||
right: 40, | ||
padding: `${tokens.spacing[20]} ${tokens.spacing[32]}`, | ||
borderRadius: 100, | ||
backgroundColor: tokens.colors.grey700, | ||
color: tokens.colors.grey0, | ||
}); | ||
|
||
export const content = style({ | ||
display: 'flex', | ||
alignItems: 'center', | ||
gap: tokens.spacing[8], | ||
}); | ||
|
||
export const message = style({ | ||
fontSize: tokens.typography.fontSize[20], | ||
fontWeight: tokens.typography.fontWeight.semibold, | ||
lineHeight: '24px', | ||
}); |
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,141 @@ | ||
import { motion, AnimatePresence, HTMLMotionProps } from 'motion/react'; | ||
import { | ||
ForwardRefExoticComponent, | ||
ReactNode, | ||
forwardRef, | ||
useEffect, | ||
KeyboardEvent, | ||
useRef, | ||
} from 'react'; | ||
import { ToastIcon } from './compounds/Icon/Icon'; | ||
import * as styles from './Toast.css'; | ||
import { useTimer } from './hooks/useTimer'; | ||
import { mergeRefs } from '@/utils'; | ||
|
||
export type ToastType = 'default' | 'success' | 'error'; | ||
|
||
export type ToastProps = { | ||
/** | ||
* 토스트 타입 | ||
* @default 'default' | ||
*/ | ||
toastType?: ToastType; | ||
/** | ||
* 왼쪽 추가 요소 (아이콘 등) | ||
*/ | ||
leftAddon?: ReactNode; | ||
/** | ||
* 토스트 지속 시간 | ||
* @default 2000 | ||
*/ | ||
duration?: number; | ||
/** | ||
* 자식 요소 | ||
*/ | ||
children?: ReactNode; | ||
/** | ||
* 토스트 열기 여부 | ||
*/ | ||
open: boolean; | ||
/** | ||
* 토스트가 열릴 때 호출되는 함수 | ||
*/ | ||
onOpen?: VoidFunction; | ||
/** | ||
* 토스트가 닫힐 때 호출되는 함수 | ||
*/ | ||
onClose?: VoidFunction; | ||
/** | ||
* 토스트가 완전히 닫힌 후 호출되는 함수 | ||
*/ | ||
onExited?: VoidFunction; | ||
} & Omit<HTMLMotionProps<'div'>, 'children'>; | ||
|
||
const ToastComponent = forwardRef<HTMLDivElement, ToastProps>( | ||
( | ||
{ | ||
toastType = 'default', | ||
leftAddon, | ||
duration = 2000, | ||
children, | ||
open, | ||
onOpen, | ||
onClose, | ||
onExited, | ||
style: toastStyle, | ||
...restProps | ||
}, | ||
ref | ||
) => { | ||
const { startCurrentTimer, clearCurrentTimeout } = useTimer({ | ||
onTimerEnd: onClose, | ||
timeoutSecond: duration, | ||
}); | ||
const toastRef = useRef<HTMLDivElement>(null); | ||
|
||
useEffect(() => { | ||
if (open) { | ||
onOpen?.(); | ||
startCurrentTimer(); | ||
toastRef.current?.focus(); | ||
} | ||
}, [open, onOpen, startCurrentTimer]); | ||
|
||
const handleKeyDown = (event: KeyboardEvent) => { | ||
if (event.key === 'Escape') { | ||
onClose?.(); | ||
} | ||
}; | ||
|
||
return ( | ||
<AnimatePresence onExitComplete={onExited}> | ||
{open && ( | ||
<motion.div | ||
ref={mergeRefs(ref, toastRef)} | ||
className={styles.container} | ||
initial={{ y: '120%', opacity: 0 }} | ||
animate={{ y: 0, opacity: 1 }} | ||
exit={{ y: '120%', opacity: 0 }} | ||
transition={{ | ||
type: 'spring', | ||
damping: 25, | ||
stiffness: 400, | ||
opacity: { | ||
duration: 0.15, | ||
ease: 'easeInOut', | ||
}, | ||
}} | ||
onPointerEnter={clearCurrentTimeout} | ||
onPointerLeave={startCurrentTimer} | ||
style={{ | ||
...toastStyle, | ||
}} | ||
role="alert" | ||
aria-live="polite" | ||
tabIndex={0} | ||
onKeyDown={handleKeyDown} | ||
{...restProps} | ||
> | ||
<div className={styles.content}> | ||
{leftAddon ?? ( | ||
<ToastIcon toastType={toastType} aria-hidden="true" /> | ||
)} | ||
<span className={styles.message}>{children}</span> | ||
</div> | ||
</motion.div> | ||
)} | ||
</AnimatePresence> | ||
); | ||
} | ||
); | ||
|
||
type ToastComposition = { | ||
Icon: typeof ToastIcon; | ||
}; | ||
|
||
export const Toast: ForwardRefExoticComponent<ToastProps> & ToastComposition = | ||
Object.assign(ToastComponent, { | ||
Icon: ToastIcon, | ||
}); | ||
|
||
Toast.displayName = 'Toast'; |
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,38 @@ | ||
import { Icon } from '@repo/ui'; | ||
import type { IconProps } from '@repo/ui'; | ||
import { ToastType } from '../../Toast'; | ||
|
||
export type ToastIconProps = Omit<IconProps, 'name'> & { | ||
toastType?: ToastType; | ||
}; | ||
|
||
export function ToastIcon({ | ||
toastType = 'default', | ||
...restProps | ||
}: ToastIconProps) { | ||
const iconName = (() => { | ||
switch (toastType) { | ||
case 'success': | ||
return 'check'; | ||
case 'error': | ||
return 'notice'; | ||
default: | ||
return null; | ||
} | ||
})(); | ||
|
||
if (!iconName) { | ||
return null; | ||
} | ||
|
||
const iconColor = (() => { | ||
switch (toastType) { | ||
case 'success': | ||
return 'violet200'; | ||
case 'error': | ||
return 'warning300'; | ||
} | ||
})(); | ||
|
||
return <Icon type="fill" name={iconName} color={iconColor} {...restProps} />; | ||
} |
Oops, something went wrong.