Skip to content

Commit

Permalink
Feat/toast snackbar combine (#65)
Browse files Browse the repository at this point in the history
* feat: 스낵바와 토스트 하나로 합침

* feat: 스낵바 스타일 정돈

* 스낵바 모바일 대응

* 의미없는 스타일 제거
  • Loading branch information
evan-moon authored Apr 3, 2021
1 parent 7471afa commit eeeee64
Show file tree
Hide file tree
Showing 18 changed files with 231 additions and 457 deletions.
2 changes: 1 addition & 1 deletion ui-kit/src/components/Button/index.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -20,7 +20,7 @@ const Button = (
className={classnames(
'lubycon-button',
`lubycon-button--${size}`,
`lubycon-button--type-${type}`
`lubycon-button--type-${type ?? 'default'}`
)}
disabled={disabled}
style={{ ...style }}
Expand Down
5 changes: 1 addition & 4 deletions ui-kit/src/components/LubyconUIKitProvider/index.tsx
Original file line number Diff line number Diff line change
@@ -1,5 +1,4 @@
import React, { ReactNode } from 'react';
import { ToastProvider } from 'contexts/Toast';
import { PortalProvider } from 'contexts/Portal';
import { SnackbarProvider } from 'src/contexts/Snackbar';
import { ModalProvider } from 'src/contexts/Modal';
Expand All @@ -12,9 +11,7 @@ function LubyconUIKitProvider({ children }: Props) {
return (
<PortalProvider>
<ModalProvider>
<SnackbarProvider>
<ToastProvider>{children}</ToastProvider>
</SnackbarProvider>
<SnackbarProvider>{children}</SnackbarProvider>
</ModalProvider>
</PortalProvider>
);
Expand Down
23 changes: 10 additions & 13 deletions ui-kit/src/components/Snackbar/SnackbarBody.tsx
Original file line number Diff line number Diff line change
@@ -1,30 +1,27 @@
import React, { ReactNode, isValidElement } from 'react';
import React, { ReactNode, isValidElement, useMemo } from 'react';
import classnames from 'classnames';
import Text from 'components/Text';
import Button from '../Button';
import { colors } from 'src/constants/colors';

interface Props {
message: string;
button: ReactNode;
button?: ReactNode;
onClick?: () => void;
}

const SnackbarBody = ({ message, button, onClick }: Props) => {
const SnackbarBody = ({ message, button: buttonProp, onClick }: Props) => {
const button = useMemo(
() =>
isValidElement(buttonProp) ? buttonProp : <Button onClick={onClick}>{buttonProp}</Button>,
[buttonProp]
);

return (
<div className={classnames('lubycon-snackbar__body', 'lubycon-shadow--3')}>
<Text typography="p2" className="lubycon-snackbar__text">
{message}
</Text>
<div className="lubycon-snackbar__body__buttons">
{isValidElement(button) ? (
button
) : (
<Button onClick={onClick} style={{ color: colors.blue50 }}>
{button}
</Button>
)}
</div>
<div className="lubycon-snackbar__body__buttons">{buttonProp == null ? null : button}</div>
</div>
);
};
Expand Down
16 changes: 11 additions & 5 deletions ui-kit/src/components/Snackbar/index.tsx
Original file line number Diff line number Diff line change
@@ -1,20 +1,24 @@
import React, { useEffect, useState, ReactNode } from 'react';
import React, { useEffect, useState, ReactNode, useMemo } from 'react';
import { animated, useTransition } from 'react-spring';
import classnames from 'classnames';
import SnackbarBody from './SnackbarBody';
import { CombineElementProps } from 'src/types/utils';
import { getTranslateAnimation } from './utils';

export type SnackbarAlign = 'left' | 'center' | 'right';

export type SnackbarProps = Omit<
CombineElementProps<
'div',
{
show: boolean;
message: string;
button: ReactNode;
button?: ReactNode;
autoHideDuration?: number;
onShow?: () => void;
onHide?: () => void;
onClick?: () => void;
align?: SnackbarAlign;
}
>,
'children'
Expand All @@ -30,26 +34,28 @@ const Snackbar = ({
onClick,
className,
style,
align = 'left',
...rest
}: SnackbarProps) => {
const [isOpen, setOpen] = useState(show);
const translateAnimation = useMemo(() => getTranslateAnimation(align), [align]);
const transition = useTransition(isOpen, null, {
from: {
opacity: 0,
transform: 'translateX(-100%)',
transform: translateAnimation.from,
height: 60,
},
enter: [
{ height: 60 },
{
opacity: 1,
transform: 'translateX(0)',
transform: translateAnimation.to,
},
],
leave: [
{
opacity: 0,
transform: 'translateX(-100%)',
transform: translateAnimation.from,
},
{ height: 0 },
],
Expand Down
21 changes: 21 additions & 0 deletions ui-kit/src/components/Snackbar/utils.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,21 @@
import { SnackbarAlign } from '.';

export const getTranslateAnimation = (align: SnackbarAlign) => {
switch (align) {
case 'left':
return {
from: 'translateX(-100%)',
to: 'translateX(0)',
};
case 'center':
return {
from: 'translateY(100%)',
to: 'translateY(0)',
};
case 'right':
return {
from: 'translateX(100%)',
to: 'translateX(0)',
};
}
};
28 changes: 0 additions & 28 deletions ui-kit/src/components/Toast/ToastBody.tsx

This file was deleted.

111 changes: 0 additions & 111 deletions ui-kit/src/components/Toast/index.tsx

This file was deleted.

2 changes: 2 additions & 0 deletions ui-kit/src/constants/colors.ts
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,8 @@ export const colors = {
gray30: '#e3e4e5',
gray20: '#f3f4f5',
gray10: '#fcfcfd',
white: '#ffffff',
black: '#000000',
} as const;

export type SemanticColor = 'positive' | 'informative' | 'negative' | 'notice';
Expand Down
60 changes: 39 additions & 21 deletions ui-kit/src/contexts/Snackbar.tsx
Original file line number Diff line number Diff line change
@@ -1,11 +1,13 @@
import React, { ReactNode, createContext, useState, useCallback, useContext } from 'react';
import classnames from 'classnames';
import Snackbar, { SnackbarProps } from 'components/Snackbar';
import Snackbar, { SnackbarAlign, SnackbarProps } from 'components/Snackbar';
import { generateID } from 'src/utils';
import { Portal } from './Portal';
interface SnackbarOptions extends Omit<SnackbarProps, 'show'> {
duration?: number;
}
import { isMatchedSM } from 'src/utils/mediaQuery';

type SnackbarOptions = Omit<SnackbarProps, 'show'>;

const aligns: SnackbarAlign[] = ['left', 'center', 'right'];

interface SnackbarGlobalState {
openSnackbar: (option: SnackbarOptions) => void;
Expand All @@ -20,12 +22,17 @@ interface SnackbarProviderProps {
children: ReactNode;
maxStack?: number;
}
export function SnackbarProvider({ children, maxStack = 1 }: SnackbarProviderProps) {
export function SnackbarProvider({ children, maxStack = 3 }: SnackbarProviderProps) {
const [openedSnackbarQueue, setOpenedSnackbarQueue] = useState<SnackbarOptions[]>([]);

const openSnackbar = useCallback(
({ id = generateID('lubycon-snackbar'), ...option }: SnackbarOptions) => {
const snackbar = { id, ...option };
({
id = generateID('lubycon-snackbar'),
align: rawAlign = 'left',
...option
}: SnackbarOptions) => {
const align = isMatchedSM() ? 'center' : rawAlign;
const snackbar = { id, align, ...option };
const [, ...rest] = openedSnackbarQueue;

if (openedSnackbarQueue.length >= maxStack) {
Expand Down Expand Up @@ -55,20 +62,31 @@ export function SnackbarProvider({ children, maxStack = 1 }: SnackbarProviderPro
>
{children}
<Portal>
<div className={classnames('lubycon-snackbar__context-container')}>
{openedSnackbarQueue.map(({ id, onHide, duration = 3000, ...snackbarProps }) => (
<Snackbar
key={id}
show={true}
autoHideDuration={duration}
onHide={() => {
closeSnackbar(id ?? '');
onHide?.();
}}
{...snackbarProps}
/>
))}
</div>
{aligns.map((align) => (
<div
key={align}
className={classnames(
'lubycon-snackbar__context-container',
`lubycon-snackbar__context-container--align-${align}`
)}
>
{openedSnackbarQueue
.filter((snackbar) => snackbar.align === align)
.map(({ id, onHide, autoHideDuration = 3000, ...snackbarProps }) => (
<Snackbar
key={id}
show={true}
autoHideDuration={autoHideDuration}
onHide={() => {
closeSnackbar(id ?? '');
onHide?.();
}}
align={align}
{...snackbarProps}
/>
))}
</div>
))}
</Portal>
</SnackbarContext.Provider>
);
Expand Down
Loading

0 comments on commit eeeee64

Please sign in to comment.