Skip to content

Commit

Permalink
feat: init toast from shadcn (#109)
Browse files Browse the repository at this point in the history
Co-authored-by: heydoyouknowme0 <[email protected]>
  • Loading branch information
GetPsyched and heydoyouknowme0 committed Apr 16, 2024
1 parent 99eb5fe commit 2e6a57b
Show file tree
Hide file tree
Showing 9 changed files with 496 additions and 3 deletions.
2 changes: 2 additions & 0 deletions app/[locale]/layout.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@ import type { Metadata } from 'next';

import Footer from '~/app/footer';
import Header from '~/app/header';
import { Toaster } from '~/components/ui';
import { cn } from '~/lib/utils';
import '~/styles/globals.css';

Expand Down Expand Up @@ -38,6 +39,7 @@ export default function RootLayout({
<Header locale={locale} />
<section className="grow">{children}</section>
<Footer locale={locale} />
<Toaster />
</body>
</html>
);
Expand Down
5 changes: 5 additions & 0 deletions components/ui/index.ts
Original file line number Diff line number Diff line change
@@ -1,6 +1,11 @@
export { Toaster } from './toaster';
export * from './toast';

export * from './button';
export * from './card';
export * from './hamburger-button';
export * from './naive-carousel';
export * from './scroll-area';
export * from './table';
export * from './toast';
export * from './toaster';
151 changes: 151 additions & 0 deletions components/ui/toast.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,151 @@
'use client';

import * as ToastPrimitives from '@radix-ui/react-toast';
import { cva, type VariantProps } from 'class-variance-authority';
import * as React from 'react';
import { RxCross2 } from 'react-icons/rx';

import { cn } from '~/lib/utils';

const ToastProvider = ToastPrimitives.Provider;

const ToastViewport = React.forwardRef<
React.ElementRef<typeof ToastPrimitives.Viewport>,
React.ComponentPropsWithoutRef<typeof ToastPrimitives.Viewport>
>(({ className, ...props }, ref) => (
<ToastPrimitives.Viewport
ref={ref}
className={cn(
'flex flex-col-reverse sm:flex-col',
'fixed top-0 sm:bottom-0 sm:right-0 sm:top-auto',
'z-toast max-h-screen w-full p-4 sm:max-w-[420px]',
className
)}
{...props}
/>
));
ToastViewport.displayName = ToastPrimitives.Viewport.displayName;

const toastVariants = cva(
cn(
'mt-2 py-3 pl-4 pr-5',
'border border-l-8 border-neutral-200 bg-background',
'flex items-center justify-between space-x-4',
'group pointer-events-auto relative w-full overflow-hidden rounded shadow-lg transition-all',
'data-[swipe=cancel]:translate-x-0',
'data-[swipe=end]:translate-x-[var(--radix-toast-swipe-end-x)]',
'data-[swipe=move]:translate-x-[var(--radix-toast-swipe-move-x)]',
'data-[swipe=move]:transition-none',
'data-[state=open]:animate-in',
'data-[state=closed]:animate-out',
'data-[swipe=end]:animate-out',
'data-[state=closed]:fade-out-80',
'data-[state=closed]:slide-out-to-right-full',
'data-[state=open]:slide-in-from-top-full',
'data-[state=open]:sm:slide-in-from-bottom-full'
),
{
variants: {
variant: {
success: 'border-success text-success',
warning: 'border-warning text-warning',
error: 'border-error text-error',
},
},
defaultVariants: {
variant: 'success',
},
}
);

const Toast = React.forwardRef<
React.ElementRef<typeof ToastPrimitives.Root>,
React.ComponentPropsWithoutRef<typeof ToastPrimitives.Root> &
VariantProps<typeof toastVariants>
>(({ className, variant, ...props }, ref) => {
return (
<ToastPrimitives.Root
ref={ref}
className={cn(toastVariants({ variant }), className)}
{...props}
/>
);
});
Toast.displayName = ToastPrimitives.Root.displayName;

const ToastAction = React.forwardRef<
React.ElementRef<typeof ToastPrimitives.Action>,
React.ComponentPropsWithoutRef<typeof ToastPrimitives.Action>
>(({ className, ...props }, ref) => (
<ToastPrimitives.Action
ref={ref}
className={cn(
'rounded-md px-3 text-sm font-medium transition-colors',
'border border-neutral-400 bg-transparent hover:bg-neutral-400',
'inline-flex h-8 shrink-0 items-center justify-center',
'ring-offset-shade-light focus:outline-none focus:ring-2 focus:ring-neutral-900 focus:ring-offset-2',
'disabled:pointer-events-none disabled:opacity-50',
className
)}
{...props}
/>
));
ToastAction.displayName = ToastPrimitives.Action.displayName;

const ToastClose = React.forwardRef<
React.ElementRef<typeof ToastPrimitives.Close>,
React.ComponentPropsWithoutRef<typeof ToastPrimitives.Close>
>(({ className, ...props }, ref) => (
<ToastPrimitives.Close
ref={ref}
className={cn(
'text-neutral-400 focus:outline-none focus:ring-2',
className
)}
toast-close=""
{...props}
>
<RxCross2 className="h-4 w-4" />
</ToastPrimitives.Close>
));
ToastClose.displayName = ToastPrimitives.Close.displayName;

const ToastTitle = React.forwardRef<
React.ElementRef<typeof ToastPrimitives.Title>,
React.ComponentPropsWithoutRef<typeof ToastPrimitives.Title>
>(({ className, ...props }, ref) => (
<ToastPrimitives.Title
ref={ref}
className={cn('text-sm font-semibold', className)}
{...props}
/>
));
ToastTitle.displayName = ToastPrimitives.Title.displayName;

const ToastDescription = React.forwardRef<
React.ElementRef<typeof ToastPrimitives.Description>,
React.ComponentPropsWithoutRef<typeof ToastPrimitives.Description>
>(({ className, ...props }, ref) => (
<ToastPrimitives.Description
ref={ref}
className={cn('text-sm text-neutral-500 opacity-90', className)}
{...props}
/>
));
ToastDescription.displayName = ToastPrimitives.Description.displayName;

type ToastProps = React.ComponentPropsWithoutRef<typeof Toast>;

type ToastActionElement = React.ReactElement<typeof ToastAction>;

export {
Toast,
ToastAction,
ToastClose,
ToastDescription,
ToastProvider,
ToastTitle,
ToastViewport,
type ToastActionElement,
type ToastProps,
};
42 changes: 42 additions & 0 deletions components/ui/toaster.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,42 @@
'use client';

import {
Toast,
ToastClose,
ToastDescription,
ToastProvider,
ToastTitle,
ToastViewport,
} from '~/components/ui';
import { useToast } from '~/lib/hooks';

export function Toaster() {
const { toasts } = useToast();

return (
<ToastProvider>
{toasts.map(function ({
id,
title,
description,
action,
showCloseBtn = true,
...props
}) {
return (
<Toast key={id} {...props} className="flex items-center">
<article className="me-auto grid">
{title && <ToastTitle>{title}</ToastTitle>}
{description && (
<ToastDescription>{description}</ToastDescription>
)}
</article>
{action}
{showCloseBtn && <ToastClose />}
</Toast>
);
})}
<ToastViewport />
</ToastProvider>
);
}
1 change: 1 addition & 0 deletions lib/hooks/index.ts
Original file line number Diff line number Diff line change
@@ -1,2 +1,3 @@
export * from './copy-to-clipboard';
export * from './ctrl-x';
export * from './toast';
Loading

0 comments on commit 2e6a57b

Please sign in to comment.