Nebula

toast

Import

import { useToast, ToastProvider } from '@xpanseinc/nebula'

Basic usage

The useToast hook is used to create, update, and programatically dismiss toasts. Your app must be wrapped in a ToastProvider.

Toasts will auto-close after a delay. The timer will reset when the user hovers over the toast. If the user clicks the close button, it will be dismissed immediately.

export const ToastExample = () => {
  const toast = useToast()
  return (
    <Button
      variant="solid"
      onClick={() =>
        toast('3 new messages', { icon: MailIcon, duration: 9000 })
      }
    >
      Show toast
    </Button>
  )
}
<ToastExample />

Creating a toast

Default

toast('3 new messages')

Creates a basic toast. It does not have an icon by default, but one can be provided using the icon prop.

Positive

toast.positive('Success!')

Create a toast with a green checkmark. The aria role is set to status.

Caution

toast.caution('Success!')

Create a toast with a yellow caution icon. The aria role is set to alert.

Negative

toast.negative('Success!')

Create a toast with a red error icon. The aria role is set to alert.

<ToastStatusExample />

Custom message render

export const ToastRenderMessageExample = () => {
  const toast = useToast()
  return (
    <Button
      variant="solid"
      onClick={() =>
        toast(
          ({ onClose }) => (
            <>
              <p css={theme => ({ ...theme.typography.body.sm, padding: 0 })}>
                New comment on your article,{' '}
                <strong>Toast and Other Interesting Topics, Unabridged</strong>
              </p>
              <a
                href="#"
                onClick={e => {
                  e.preventDefault()
                  onClose()
                }}
                css={theme => ({
                  color: theme.color.primary.mid,
                  fontWeight: theme.fontWeight.bold,
                  ':hover': {
                    color: theme.color.primary.light,
                  },
                })}
              >
                View comment
              </a>
            </>
          ),
          { status: 'neutral', duration: Infinity },
        )
      }
    >
      Show toast
    </Button>
  )
}
<ToastRenderMessageExample />

Toast options

toast(
  message: string | React.ReactNode,
  options?: {
  duration?: number = 15000 // 15s. To keep open indefinitely, set to Infinity
  hideCloseButton?: boolean
  status?: 'default' | 'neutral' | 'positive' | 'caution' | 'negative' = 'default'
  icon?: Icon
  onCloseComplete?: () => void
}): ToastId

Update toast

export const UpdateToastExample = () => {
  const toast = useToast()
  const toastId = useRef(null)
  useEffect(() => {
    return () => {
      if (toast.isActive(toastId.current)) {
        toast.dismiss(toastId.current)
      }
    }
  }, [])
  return (
    <HStack spacing={16}>
      <Button
        variant="solid"
        onClick={async () => {
          if (!toast.isActive(toastId.current)) {
            toastId.current = toast('Receiving transmission…', {
              icon: BoltIcon,
              duration: Infinity,
              hideCloseButton: true,
              onCloseComplete: () => {
                toastId.current = null
              },
            })
            await sleep(1500)
            toast.update(toastId.current, {
              status: 'positive',
              message: 'Transmission received',
              options: {
                status: 'positive',
                duration: 3000,
                icon: null,
                hideCloseButton: false,
              },
            })
          }
        }}
      >
        Show toast
      </Button>
    </HStack>
  )
}
<UpdateToastExample />

Programatically dismiss

export const DismissToastExample = () => {
  const toast = useToast()
  const toastId = useRef(null)
  const handleClick = useCallback(async () => {
    if (!toastId.current) {
      toastId.current = toast('Sending transmission…', {
        icon: BoltIcon,
        duration: Infinity,
        hideCloseButton: true,
        onCloseComplete: () => {
          toastId.current = null
        },
      })
      await sleep(1500)
      toast.dismiss(toastId.current)
    }
  }, [toastId, toast])
  return (
    <Button variant="solid" onClick={handleClick}>
      Show toast
    </Button>
  )
}
<DismissToastExample />

Check if toast is active

toast.isActive(toastId): boolean

ToastProvider

The ToastProvider manages the state and displaying of toasts. By default, it will limit the number of toasts displayed to 5. This can be adjusted using the limit prop. It will display newer notifications first and queue older notifications.

Toasts are postitioned in the bottom-left of the screen. This position can be adjusted using the offset prop.

<ToastProvider limit={3} offset={{ x: 256, y: 100 }}>
  {children}
</ToastProvider>

Toast

General notification
Neutral notification
Positive notification
<AnimatePresence initial={false}>
  <VStack spacing={16}>
    <Toast icon={BoltIcon} message="General notification" />

    <Toast status="neutral" message="Neutral notification" />

    <Toast status="positive" message="Positive notification" />

    <Toast status="caution" message="Caution notification" />

    <Toast status="negative" message="Negative notification" />
  </VStack>
</AnimatePresence>