Skip to content

Commit

Permalink
feat(common): toast component position props
Browse files Browse the repository at this point in the history
  • Loading branch information
kyechan99 committed Feb 25, 2024
1 parent b238cc1 commit f55e719
Show file tree
Hide file tree
Showing 5 changed files with 100 additions and 16 deletions.
41 changes: 36 additions & 5 deletions src/components/common/Toast/Toast.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -5,10 +5,11 @@ import React from 'react';
import { DefaultVariantType } from '@/types/variant';

import { useToast } from '.';
import { ToastData } from './type';
import { ToastData, ToastPosition } from './type';

interface ToastProps extends React.ComponentPropsWithoutRef<'li'> {
toast: ToastData;
position: ToastPosition;
}

export const Toast = React.forwardRef<HTMLLIElement, ToastProps>(({ toast, ...props }, ref) => {
Expand Down Expand Up @@ -85,7 +86,37 @@ const ToastVariantStyles = ({ theme, variant }: { theme: Theme; variant: Default
}
};

const ToastStyled = styled.li<{ variant?: DefaultVariantType }>`
const ToastPositionStyles = ({ position }: { position: ToastPosition }) => {
switch (position) {
case 'top-left':
return css`
transform: translateX(calc(-100% - 2rem));
`;
case 'top-center':
return css`
transform: translateY(-100vh);
`;
case 'top-right':
return css`
transform: translateX(calc(100% + 2rem));
`;
case 'bottom-left':
return css`
transform: translateX(calc(-100% - 2rem));
`;
case 'bottom-center':
return css`
transform: translateY(100vh);
`;
case 'bottom-right':
default:
return css`
transform: translateX(calc(100% + 2rem));
`;
}
};

const ToastStyled = styled.li<{ variant?: DefaultVariantType; position?: ToastPosition }>`
padding: 1rem;
background-color: ${({ theme }) => theme.colors.white};
border-radius: 0.5rem;
Expand All @@ -96,14 +127,14 @@ const ToastStyled = styled.li<{ variant?: DefaultVariantType }>`
justify-content: space-between;
box-shadow: 0 0px 3px ${({ theme }) => theme.colors.gray['400']};
transition: transform 0.25s cubic-bezier(0.75, -0.5, 0.25, 1.25);
transform: translateX(calc(100% + 2rem));
transition: all 0.25s cubic-bezier(0.75, -0.5, 0.25, 1.25);
cursor: pointer;
&.active {
transform: translateX(0);
transform: translateX(0) translateY(0);
}
${({ theme, variant }) => variant && ToastVariantStyles({ theme, variant })}
${({ position }) => position && ToastPositionStyles({ position })}
`;

export const ToastTitle = styled.div`
Expand Down
65 changes: 58 additions & 7 deletions src/components/common/Toast/Toaster.tsx
Original file line number Diff line number Diff line change
@@ -1,32 +1,84 @@
import { css } from '@emotion/react';
import styled from '@emotion/styled';
import React from 'react';
import { createPortal } from 'react-dom';

import { Toast } from './Toast';
import { ToasterProvider } from './ToastContext';
import { ToastPosition } from './type';
import { useToast } from './useToast';

interface ToasterProps {
position?: ToastPosition;
}

/**
* Toast Context Provider
* @returns
*/
export const Toaster = () => {
export const Toaster = ({ position = 'bottom-right' }: ToasterProps) => {
const { toasts } = useToast();

return (
<ToasterProvider>
{toasts.length > 0 &&
createPortal(
<ToastList tabIndex={-1}>
<ToastList tabIndex={-1} position={position}>
{toasts.map(t => (
<Toast key={t.toastId} toast={t} />
<Toast key={t.toastId} toast={t} position={position} />
))}
</ToastList>,
document.getElementById('toaster') || document.body,
)}
</ToasterProvider>
);
};
const ToastList = styled.ol`

const ToastPositionStyles = ({ position }: { position: ToastPosition }) => {
switch (position) {
case 'top-left':
return css`
top: 0px;
left: 0px;
bottom: auto;
`;
case 'top-center':
return css`
top: 0px;
left: 50%;
bottom: auto;
transform: translate(-50%);
`;
case 'top-right':
return css`
top: 0px;
right: 0px;
bottom: auto;
`;
case 'bottom-left':
return css`
top: auto;
left: 0px;
bottom: 0px;
`;
case 'bottom-center':
return css`
bottom: 0px;
left: 50%;
top: auto;
transform: translate(-50%);
`;
case 'bottom-right':
default:
return css`
top: auto;
right: 0px;
bottom: 0px;
`;
}
};

const ToastList = styled.ol<{ position?: ToastPosition }>`
list-style: none;
position: fixed;
display: flex;
Expand All @@ -35,10 +87,9 @@ const ToastList = styled.ol`
max-height: 100vh;
width: 100%;
padding: 1rem;
top: auto;
right: 0px;
bottom: 0px;
flex-direction: column;
max-width: 25rem;
gap: 1rem;
${({ position }) => position && ToastPositionStyles({ position })}
`;
2 changes: 2 additions & 0 deletions src/components/common/Toast/type.ts
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,8 @@ import { Toast } from '.';

export type ToastProps = React.ComponentPropsWithoutRef<typeof Toast>;

export type ToastPosition = 'top-left' | 'top-center' | 'top-right' | 'bottom-left' | 'bottom-center' | 'bottom-right';

export const TOAST_LIMIT = 5;

// export Type
Expand Down
4 changes: 2 additions & 2 deletions src/components/common/Toast/useToast.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -9,12 +9,12 @@ const listeners: Array<(state: State) => void> = [];

let memoryState: State = { toasts: [] };

function dispatch(action: Action) {
const dispatch = (action: Action) => {
memoryState = reducer(memoryState, action);
listeners.forEach(listener => {
listener(memoryState);
});
}
};

let cnt = 1;
const toast = ({ ...props }: ToastProps) => {
Expand Down
4 changes: 2 additions & 2 deletions src/stories/common/Toast.stories.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -30,7 +30,7 @@ const meta = {
return (
<>
<Story />
<Toaster />
<Toaster position="bottom-right" />
</>
);
},
Expand All @@ -47,7 +47,7 @@ type Story = StoryObj<typeof ToastForStory>;
export const Default: Story = {
args: {
title: 'Toast Title',
description: `When you click Toast, containing 'data' 'action' is executed.`,
description: `When you click Toast, containing 'data' 'action' is executed.`,
variant: 'primary',
time: 5000,
data: 'Hi',
Expand Down

0 comments on commit f55e719

Please sign in to comment.