diff --git a/packages/ui/lib/components/Toast/index.tsx b/packages/ui/lib/components/Toast/index.tsx new file mode 100644 index 00000000000..e503cfd8388 --- /dev/null +++ b/packages/ui/lib/components/Toast/index.tsx @@ -0,0 +1,84 @@ +import { + Renderable, + toast, + Toast, + ToastOptions, + Toaster, + ValueOrFunction, +} from 'react-hot-toast' +import { ReactNode } from 'react' + +export interface ToastHelperProps { + success: (message: string) => void + error: (message: string) => void + promise: ( + promise: Promise, + msgs: { + loading: Renderable + success: ValueOrFunction + error: ValueOrFunction + }, + opts?: + | Partial< + Pick< + Toast, + | 'id' + | 'icon' + | 'duration' + | 'ariaProps' + | 'className' + | 'style' + | 'position' + | 'iconTheme' + > + > + | undefined + ) => Promise + redirectErrorPage: (errorPage: '404' | '500') => void +} + +const options: ToastOptions = { + style: { + wordBreak: 'break-word', + }, +} + +export const ToastHelper: ToastHelperProps = { + success: (message) => toast.success(message, options), + error: (message) => toast.error(message, options), + promise: async (promise, msgs, opts = {}) => { + const start = new Date().getTime() + const result = await toast.promise(promise, msgs, opts) + if (new Date().getTime() - start < 300) { + toast.remove() // This cancels the toast immediately + } + return result + }, + redirectErrorPage: (page) => { + const redirectPage = `/${page}` + window.location.href = redirectPage + }, +} + +interface ToastProviderProps { + children: ReactNode + position?: + | 'top-left' + | 'top-right' + | 'top-center' + | 'bottom-left' + | 'bottom-right' + | 'bottom-center' +} + +export function ToastProvider({ + children, + position = 'top-center', +}: ToastProviderProps) { + return ( + <> + {children} + + + ) +} diff --git a/packages/ui/lib/index.tsx b/packages/ui/lib/index.tsx index d1c931ecd61..40558791a82 100644 --- a/packages/ui/lib/index.tsx +++ b/packages/ui/lib/index.tsx @@ -46,3 +46,4 @@ export { PriceFormatter } from '~/components/PriceFormatter/PriceFormatter' export { CurrencyHint } from '~/components/Text/CurrencyHint' export { Address } from '~/components/Display/Address' export { Combobox } from '~/components/Form/Combobox' +export { ToastHelper, ToastProvider } from '~/components/Toast' diff --git a/packages/ui/package.json b/packages/ui/package.json index 286453e4fde..6e0426b0639 100644 --- a/packages/ui/package.json +++ b/packages/ui/package.json @@ -41,6 +41,7 @@ "prism-react-renderer": "2.4.1", "react-dropzone": "14.3.5", "react-hook-form": "7.54.2", + "react-hot-toast": "2.5.2", "react-icons": "5.4.0", "react-use-clipboard": "1.0.9", "tailwind-merge": "3.0.1", diff --git a/yarn.lock b/yarn.lock index f721df853c8..49e3b5cd88f 100644 --- a/yarn.lock +++ b/yarn.lock @@ -19128,6 +19128,7 @@ __metadata: react-dom: "npm:18.3.1" react-dropzone: "npm:14.3.5" react-hook-form: "npm:7.54.2" + react-hot-toast: "npm:2.5.2" react-icons: "npm:5.4.0" react-use-clipboard: "npm:1.0.9" storybook: "npm:8.5.3" @@ -31620,7 +31621,7 @@ __metadata: languageName: node linkType: hard -"goober@npm:^2.1.10": +"goober@npm:^2.1.10, goober@npm:^2.1.16": version: 2.1.16 resolution: "goober@npm:2.1.16" peerDependencies: @@ -44469,6 +44470,19 @@ __metadata: languageName: node linkType: hard +"react-hot-toast@npm:2.5.2": + version: 2.5.2 + resolution: "react-hot-toast@npm:2.5.2" + dependencies: + csstype: "npm:^3.1.3" + goober: "npm:^2.1.16" + peerDependencies: + react: ">=16" + react-dom: ">=16" + checksum: 10/fde6aaa26d85a8de6fbc2b62c66fd71da555b389e8276b0b1e3636f0bf2956e57f40d7692959f2fb0ee91000ae44cad2ab5add779ef0e9b7fc9eb148203dca54 + languageName: node + linkType: hard + "react-icons@npm:5.3.0": version: 5.3.0 resolution: "react-icons@npm:5.3.0"