Skip to content

Commit

Permalink
refactor: 토스트 로직 수정
Browse files Browse the repository at this point in the history
  • Loading branch information
sikkzz committed Feb 21, 2025
1 parent 55c60d2 commit 93256a8
Show file tree
Hide file tree
Showing 5 changed files with 63 additions and 87 deletions.
72 changes: 20 additions & 52 deletions src/components/ui/Toast/Toast.module.scss
Original file line number Diff line number Diff line change
Expand Up @@ -19,61 +19,17 @@
}
}

// .Toast {
// width: 100%;
// height: 3.25rem;
// z-index: 2;
// border-radius: 0.75rem;
// background-color: white;
// padding: 0.875rem 1.125rem;

// transition:
// opacity 0.3s,
// transform 0.3s;
// }

// .show {
// opacity: 1;
// transform: translateY(0);
// }

// .ToastRoot[data-state="open"] {
// animation: slideUp 150ms cubic-bezier(0.16, 1, 0.3, 1);
// }
// .ToastRoot[data-state="closed"] {
// animation: hide 100ms ease-in;
// }

// @keyframes hide {
// from {
// opacity: 1;
// }
// to {
// opacity: 0;
// }
// }

// @keyframes slideUp {
// from {
// transform: translateY(100%);
// opacity: 0;
// }
// to {
// transform: translateY(0);
// opacity: 1;
// }
// }

.ToastViewport {
position: fixed;
left: 50%;
top: 0.75rem;
bottom: 7rem;
z-index: 9999;
width: fit-content;
display: flex;
flex-direction: column;
gap: 0.5rem;
transform: translateX(-50%);
width: calc(100% - 2.5rem);
}

.Toast {
Expand All @@ -87,31 +43,43 @@
transform: translateX(var(--radix-toast-swipe-move-x));
}
&[data-state="open"] {
animation: slide-in-from-top 0.3s ease-out forwards;
animation: slide-in-from-bottom-full 0.4s ease-out forwards;
}

&[data-state="closed"] {
animation: slide-out-to-top 0.3s ease-in forwards;
animation: slide-out-to-bottom-full 0.4s ease-in forwards;
}
}

@keyframes slide-in-from-top {
@keyframes slide-in-from-bottom-full {
from {
opacity: 0;
transform: translateY(-10px);
transform: translateY(300%);
}
to {
opacity: 1;
transform: translateY(0);
}
}

@keyframes slide-out-to-top {
@keyframes slide-out-to-bottom-full {
from {
opacity: 1;
transform: translateY(0);
}
to {
opacity: 0;
transform: translateY(-10px);
transform: translateY(300%);
}
}

.Toaster {
border-radius: 0.75rem;
background-color: var(--color-white);
width: 100%;
padding: 0.875rem 1.125rem;

& > span {
line-height: 1.5rem;
}
}
20 changes: 0 additions & 20 deletions src/components/ui/Toast/Toast.stories.tsx

This file was deleted.

26 changes: 20 additions & 6 deletions src/components/ui/Toast/Toast.tsx
Original file line number Diff line number Diff line change
@@ -1,13 +1,15 @@
import * as React from "react";
import { forwardRef } from "react";

import * as ToastPrimitives from "@radix-ui/react-toast";
import classNames from "classnames";

import styles from "@/components/ui/Toast/Toast.module.scss";

import { useToast } from "@/hooks/common/useToast";

const ToastProvider = ToastPrimitives.Provider;

const ToastViewport = React.forwardRef<
const ToastViewport = forwardRef<
React.ElementRef<typeof ToastPrimitives.Viewport>,
React.ComponentPropsWithoutRef<typeof ToastPrimitives.Viewport>
>(({ className, ...props }, ref) => (
Expand All @@ -19,17 +21,29 @@ const ToastViewport = React.forwardRef<
));
ToastViewport.displayName = ToastPrimitives.Viewport.displayName;

const Toast = React.forwardRef<
const Toast = forwardRef<
React.ElementRef<typeof ToastPrimitives.Root>,
React.ComponentPropsWithoutRef<typeof ToastPrimitives.Root>
>(({ className, ...props }, ref) => {
>(({ className, open, id, ...props }, ref) => {
const { removeToast } = useToast();

return (
<ToastPrimitives.Root ref={ref} className={classNames(styles.Toast, className)} {...props} />
<ToastPrimitives.Root
ref={ref}
open={open}
onOpenChange={(isOpen) => {
if (!isOpen) {
setTimeout(() => removeToast(id), 400); // 0.4초 뒤 제거
}
}}
className={classNames(styles.Toast, className)}
{...props}
/>
);
});
Toast.displayName = ToastPrimitives.Root.displayName;

const ToastTitle = React.forwardRef<
const ToastTitle = forwardRef<
React.ElementRef<typeof ToastPrimitives.Title>,
React.ComponentPropsWithoutRef<typeof ToastPrimitives.Title>
>(({ className, ...props }, ref) => (
Expand Down
20 changes: 14 additions & 6 deletions src/components/ui/Toast/Toaster.tsx
Original file line number Diff line number Diff line change
@@ -1,4 +1,6 @@
import { Toast, ToastProvider, ToastTitle, ToastViewport } from "@/components/ui/Toast/Toast";
import Text from "@/components/ui/Text/Text";
import { Toast, ToastProvider, ToastViewport } from "@/components/ui/Toast/Toast";
import styles from "@/components/ui/Toast/Toast.module.scss";

import { useToast } from "@/hooks/common/useToast";

Expand All @@ -7,15 +9,21 @@ export function Toaster() {

return (
<ToastProvider swipeDirection="right">
{toasts.map(({ id, title, duration }) => (
{toasts.map(({ id, duration, open }) => (
<Toast
key={id}
open
open={open}
duration={duration}
onOpenChange={(open) => !open && removeToast(id)}
className="flex items-center gap-3 rounded-3 bg-white px-3 py-2 shadow-1"
onOpenChange={(isOpen) => {
if (!isOpen) {
setTimeout(() => removeToast(id), 400);
}
}}
className={styles.Toaster}
>
{title && <ToastTitle className="text-caption-1-bold text-black">{title}</ToastTitle>}
<Text color="primary" variant="bodyM">
링크가 복사되었어요.
</Text>
</Toast>
))}
<ToastViewport />
Expand Down
12 changes: 9 additions & 3 deletions src/hooks/common/useToast.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,7 @@ type ToastType = {
type ToastContextType = {
toasts: ToastType[];
addToast: (title: string, duration?: number) => string;
removeToast: (id: string) => void;
removeToast: (id?: string) => void;
};

const ToastContext = createContext<ToastContextType | undefined>(undefined);
Expand All @@ -22,6 +22,12 @@ export function ToastProvider({ children }: { children: ReactNode }) {
const addToast = (title: string, duration = 3000): string => {
const id = crypto.randomUUID();

const isToastOpen = toasts.some((toast) => toast.open);

if (isToastOpen) {
return "";
}

const newToast: ToastType = {
id,
title,
Expand All @@ -33,8 +39,8 @@ export function ToastProvider({ children }: { children: ReactNode }) {
return id;
};

const removeToast = (id: string) => {
setToasts((prev) => prev.filter((toast) => toast.id !== id));
const removeToast = (id?: string) => {
setToasts((prev) => prev.map((toast) => (toast.id === id ? { ...toast, open: false } : toast)));
};

return (
Expand Down

0 comments on commit 93256a8

Please sign in to comment.