diff --git a/apps/webapp/app/components/primitives/Toast.tsx b/apps/webapp/app/components/primitives/Toast.tsx index 133bf6507e..8a16cc9847 100644 --- a/apps/webapp/app/components/primitives/Toast.tsx +++ b/apps/webapp/app/components/primitives/Toast.tsx @@ -1,7 +1,7 @@ import { ExclamationCircleIcon, XMarkIcon } from "@heroicons/react/20/solid"; import { CheckCircleIcon } from "@heroicons/react/24/solid"; -import { AnimatePresence, motion } from "framer-motion"; -import toast, { Toaster, resolveValue, useToasterStore } from "react-hot-toast"; +import { Toaster, toast } from "sonner"; + import { useTypedLoaderData } from "remix-typedjson"; import { loader } from "~/root"; import { useEffect } from "react"; @@ -11,79 +11,55 @@ const permanentToastDuration = 60 * 60 * 24 * 1000; export function Toast() { const { toastMessage } = useTypedLoaderData(); - useEffect(() => { if (!toastMessage) { return; } const { message, type, options } = toastMessage; - switch (type) { - case "success": - toast.success(message, { - duration: options.ephemeral ? defaultToastDuration : permanentToastDuration, - }); - break; - case "error": - toast.error(message, { - duration: options.ephemeral ? defaultToastDuration : permanentToastDuration, - }); - break; - default: - throw new Error(`${type} is not handled`); - } + toast.custom((t) => , { + duration: options.ephemeral ? defaultToastDuration : permanentToastDuration, + }); }, [toastMessage]); + return ; +} + +export function ToastUI({ + variant, + message, + t, + toastWidth = 356, // Default width, matches what sonner provides by default +}: { + variant: "error" | "success"; + message: string; + t: string; + toastWidth?: string | number; +}) { return ( - , - }, - error: { - icon: , - }, +
- {(t) => ( - - - {t.icon} - {resolveValue(t.message, t)} - - - - )} - +
+ {variant === "success" ? ( + + ) : ( + + )} + {message} + +
+
); } diff --git a/apps/webapp/app/components/stories/ToastUI.stories.tsx b/apps/webapp/app/components/stories/ToastUI.stories.tsx new file mode 100644 index 0000000000..c4a7164d80 --- /dev/null +++ b/apps/webapp/app/components/stories/ToastUI.stories.tsx @@ -0,0 +1,47 @@ +import type { Meta, StoryObj } from "@storybook/react"; +import { Toaster, toast } from "sonner"; +import { ToastUI } from "../primitives/Toast"; +import { Button } from "../primitives/Buttons"; + +const meta: Meta = { + title: "Primitives/Toast", +}; + +export default meta; + +type Story = StoryObj; + +export const Toasts: Story = { + render: () => , +}; + +function Collection() { + return ( +
+ + +
+ + + +
+ ); +} diff --git a/apps/webapp/package.json b/apps/webapp/package.json index a9cc3baaa9..cf9f0b53ea 100644 --- a/apps/webapp/package.json +++ b/apps/webapp/package.json @@ -106,6 +106,7 @@ "simple-oauth2": "^5.0.0", "simplur": "^3.0.1", "slug": "^6.0.0", + "sonner": "^1.0.3", "tailwind-merge": "^1.12.0", "tailwind-scrollbar-hide": "^1.1.7", "tailwindcss-animate": "^1.0.5", diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index 17000ef6bf..2f9c3b44c5 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -182,6 +182,7 @@ importers: simple-oauth2: ^5.0.0 simplur: ^3.0.1 slug: ^6.0.0 + sonner: ^1.0.3 storybook: ^7.0.7 storybook-addon-designs: 7.0.0-beta.2 storybook-addon-variants: ^0.2.0 @@ -275,6 +276,7 @@ importers: simple-oauth2: 5.0.0 simplur: 3.0.1 slug: 6.1.0 + sonner: 1.0.3_biqbaboplfbrettd7655fr4n2y tailwind-merge: 1.12.0 tailwind-scrollbar-hide: 1.1.7 tailwindcss-animate: 1.0.5_tailwindcss@3.3.2 @@ -28918,6 +28920,16 @@ packages: atomic-sleep: 1.0.0 dev: true + /sonner/1.0.3_biqbaboplfbrettd7655fr4n2y: + resolution: {integrity: sha512-hBoA2zKuYW3lUnpx4K0vAn8j77YuYiwvP9sLQfieNS2pd5FkT20sMyPTDJnl9S+5T27ZJbwQRPiujwvDBwhZQg==} + peerDependencies: + react: ^18.0.0 + react-dom: ^18.0.0 + dependencies: + react: 18.2.0 + react-dom: 18.2.0_react@18.2.0 + dev: false + /sorcery/0.11.0: resolution: {integrity: sha512-J69LQ22xrQB1cIFJhPfgtLuI6BpWRiWu1Y3vSsIwK/eAScqJxd/+CJlUuHQRdX2C9NGFamq+KqNywGgaThwfHw==} hasBin: true