{ "$schema": "https://ui.shadcn.com/schema/registry-item.json", "name": "notify", "dependencies": [ "lucide-react", "framer-motion", "class-variance-authority", "clsx", "tailwind-merge" ], "files": [ { "path": "packages/notify/src/notify.tsx", "content": "\"use client\"\n\nimport * as React from \"react\"\nimport { Loader, LoaderCircle, X } from \"lucide-react\"\n\nimport { cn } from \"./cn\"\nimport { ToastClassNames, ToastParams } from \"./notify-types\"\nimport {\n progressBarVariants,\n toastActionVariants,\n toastVariants,\n} from \"./notify-variants\"\n\nconst ToastTitle = React.forwardRef<\n HTMLParagraphElement,\n React.HTMLAttributes & { text: React.ReactNode }\n>(({ text, className, ...props }, ref) => (\n \n {text}\n

\n))\n\nconst ToastDescription = React.forwardRef<\n HTMLParagraphElement,\n React.HTMLAttributes & {\n description?: ToastParams[\"description\"]\n status: ToastParams[\"status\"]\n }\n>(({ description, status, className, ...props }, ref) => {\n if (!description || status === \"loading\") return null\n return (\n \n {description}\n

\n )\n})\n\nconst ToastCloseButton = React.forwardRef<\n HTMLButtonElement,\n React.ButtonHTMLAttributes & {\n onClose: () => void\n status: string\n }\n>(({ onClose, status, className, ...props }, ref) => {\n if (status === \"loading\") return null\n return (\n \n \n Close\n \n )\n})\n\ntype ToastLoaderProps = {\n status: string\n variant?: ToastParams[\"loaderVariant\"]\n className?: string\n}\n\nconst ToastLoader = ({\n status,\n variant = \"loader-1\",\n className,\n}: ToastLoaderProps) => {\n if (status !== \"loading\") return null\n const baseClass = \"animate-spin size-4 text-black\"\n\n return variant === \"loader-2\" ?\n \n : \n}\n\nconst ToastProgressBar = React.forwardRef<\n HTMLDivElement,\n React.HTMLAttributes & {\n duration?: number\n status: ToastParams[\"status\"]\n hideProgressBar?: boolean\n paused?: boolean\n }\n>(({ duration, status, hideProgressBar, className, paused, ...props }, ref) => {\n if (!duration || status === \"loading\" || hideProgressBar) return null\n return (\n \n \n \n )\n})\n\nexport function ToastAction({\n label,\n onClick,\n variant,\n status,\n}: {\n label: string\n onClick: () => void\n variant: \"primary\" | \"dismiss\"\n status: ToastParams[\"status\"]\n}) {\n return (\n \n {label}\n \n )\n}\n\n// Animation variants are now handled directly in the Toast provider\n\nexport function Toast({\n closable,\n description,\n duration,\n onClose,\n text,\n title,\n status = \"default\",\n loaderVariant,\n classNames = {},\n hideProgressBar,\n actions,\n paused,\n}: ToastParams & { classNames?: ToastClassNames; paused?: boolean }) {\n return (\n \n
\n \n
\n \n \n
\n {closable && !actions && (\n \n )}\n
\n {actions && (\n
\n {actions.dismiss && (\n \n )}\n \n
\n )}\n \n \n )\n}\n\nToastTitle.displayName = \"ToastTitle\"\nToastDescription.displayName = \"ToastDescription\"\nToastCloseButton.displayName = \"ToastCloseButton\"\nToastLoader.displayName = \"ToastLoader\"\nToastProgressBar.displayName = \"ToastProgressBar\"\n", "type": "registry:ui", "target": "components/ui/notify/notify.tsx" }, { "path": "packages/notify/src/notify-provider.tsx", "content": "\"use client\"\n\nimport { useEffect, useState } from \"react\"\nimport { AnimatePresence, motion } from \"framer-motion\"\nimport { createPortal } from \"react-dom\"\n\nimport { cn } from \"./cn\"\nimport { Toast } from \"./notify\"\nimport { getAnimationProps } from \"./notify-animations\"\nimport { DEFAULT_CONFIG } from \"./notify-config\"\nimport { useToastStateManager } from \"./notify-state-manager\"\nimport type {\n PromiseHandler,\n ToastPosition,\n ToastProviderProps,\n} from \"./notify-types\"\nimport { toast } from \"./notify-utils\"\nimport { toastPositionVariants } from \"./notify-variants\"\n\n/**\n * Portal component that mounts toast notifications to the document body\n * @param children Content to render within the portal\n */\nfunction ToastPortal({ children }: { children: React.ReactNode }) {\n const [mounted, setMounted] = useState(false)\n\n useEffect(() => {\n setMounted(true)\n }, [])\n\n return mounted ? createPortal(children, document.body) : null\n}\n\n/**\n * Provider component for toast notifications\n * Must wrap the application to enable toast functionality\n *\n * @param children Child components to render\n * @param position Default position for toasts\n * @param duration Default duration in milliseconds\n * @param classNames Default CSS class names\n * @param closable Whether toasts are closable by default\n * @param preventDuplicates Whether to prevent duplicate toasts\n * @param maxToast Maximum number of toasts to show at once\n * @param hideProgressBar Whether to hide progress bars\n * @param animation Default animation style\n */\nexport function ToastProvider({\n children,\n position: defaultPosition = DEFAULT_CONFIG.position,\n duration = DEFAULT_CONFIG.duration,\n classNames = {},\n closable = DEFAULT_CONFIG.closable,\n preventDuplicates = DEFAULT_CONFIG.preventDuplicates,\n maxToast = DEFAULT_CONFIG.maxToast,\n hideProgressBar = DEFAULT_CONFIG.hideProgressBar,\n animation = DEFAULT_CONFIG.animation,\n}: ToastProviderProps) {\n const {\n toastsByPosition,\n push,\n pushPromise,\n dismiss,\n pauseToast,\n resumeToast,\n } = useToastStateManager({\n position: defaultPosition,\n duration,\n classNames,\n closable,\n preventDuplicates,\n maxToast,\n hideProgressBar,\n animation,\n })\n\n // Register the toast handlers\n useEffect(() => {\n toast.setHandlers(push, pushPromise as PromiseHandler, dismiss)\n\n // eslint-disable-next-line react-hooks/exhaustive-deps\n }, [])\n\n const toastContainer = (\n
\n {Object.entries(toastsByPosition).map(([position, positionToasts]) => (\n \n \n {positionToasts.map((toast) => {\n const { id, params, paused } = toast\n const toastAnimation = params.animation || animation\n const toastPosition = params.position || position\n\n const animationProps = getAnimationProps(\n toastAnimation,\n toastPosition as ToastPosition\n )\n\n return (\n pauseToast(id)}\n onMouseLeave={() => resumeToast(id)}\n drag\n dragConstraints={{ left: 0, right: 0, top: 0, bottom: 0 }}\n dragElastic={0.5}\n onDragEnd={(_, info) => {\n const threshold = 0\n const { x, y } = info.offset\n if (Math.abs(x) > threshold || Math.abs(y) > threshold) {\n dismiss(id)\n }\n }}\n >\n dismiss(id)}\n paused={paused}\n />\n \n )\n })}\n \n
\n ))}\n \n )\n\n return (\n \n {children}\n {toastContainer}\n \n )\n}\n", "type": "registry:ui", "target": "components/ui/notify/notify-provider.tsx" }, { "path": "packages/notify/src/notify-utils.ts", "content": "\"use client\"\n\nimport type {\n PromiseHandler,\n ToastFunction,\n ToastMethods,\n ToastParams,\n ToastPromiseOptions,\n} from \"./notify-types\"\n\n/**\n * Simple ID generator for toast notifications\n */\nlet toastId = 0\n\n/**\n * Generate a unique ID for a toast\n */\nexport const generateId = () => String(toastId++)\n\n/**\n * Toast API class implementing ToastMethods interface\n * Provides a single point of access for creating and managing toast notifications\n */\nclass Toast implements ToastMethods {\n private emit: ((params: ToastParams) => string) | null = null\n private emitPromise: PromiseHandler | null = null\n private emitDismiss: ((id: string | number) => void) | null = null\n\n /**\n * Sets up the handlers for the Toast API\n * Called by ToastProvider on initialization\n */\n setHandlers(\n emit: (params: ToastParams) => string,\n promiseHandler: PromiseHandler,\n dismissHandler: (id: string | number) => void\n ) {\n this.emit = emit\n this.emitPromise = promiseHandler\n this.emitDismiss = dismissHandler\n }\n\n private createToastFn(status: ToastParams[\"status\"]): ToastFunction {\n return (text, options = {}) => {\n if (!this.emit) {\n throw new Error(\n \"Toast not initialized: wrap your app with ToastProvider\"\n )\n }\n return this.emit({\n text,\n status,\n ...options,\n })\n }\n }\n\n /**\n * Display a success toast notification\n * @param text - The main message to display\n * @param options - Optional configuration for the toast\n * @example\n * toast.success('Profile updated successfully')\n * toast.success('Files uploaded', { duration: 5000, description: '3 files uploaded' })\n */\n success = this.createToastFn(\"success\")\n\n /**\n * Display an error toast notification\n * @param text - The error message to display\n * @param options - Optional configuration for the toast\n * @example\n * toast.error('Failed to save changes')\n * toast.error('Upload failed', { description: 'Network error occurred' })\n */\n error = this.createToastFn(\"error\")\n\n /**\n * Display a warning toast notification\n * @param text - The warning message to display\n * @param options - Optional configuration for the toast\n * @example\n * toast.warning('Low storage space')\n * toast.warning('Session expiring', { description: 'Please save your work' })\n */\n warning = this.createToastFn(\"warning\")\n\n /**\n * Display an info toast notification\n * @param text - The information message to display\n * @param options - Optional configuration for the toast\n * @example\n * toast.info('New updates available')\n * toast.info('Tips', { description: 'Swipe left to delete' })\n */\n info = this.createToastFn(\"info\")\n\n /**\n * Display a loading toast notification\n * @param text - The loading message to display\n * @param options - Optional configuration for the toast\n * @example\n * toast.loading('Uploading files...')\n * toast.loading('Processing', { duration: Infinity })\n */\n loading = this.createToastFn(\"loading\")\n\n /**\n * Display a default toast notification\n * @param text - The message to display\n * @param options - Optional configuration for the toast\n * @example\n * toast.default('Something happened')\n */\n default = this.createToastFn(\"default\")\n\n /**\n * Dismiss a toast notification by its ID\n * @param id - The ID of the toast to dismiss\n * @example\n * const toastId = toast.info('Loading...');\n * toast.dismiss(toastId);\n */\n dismiss = (id: string | number) => {\n if (this.emitDismiss) {\n this.emitDismiss(id)\n }\n }\n\n /**\n * Create a custom toast notification with full control over its properties\n * @param params - Complete toast parameters\n * @example\n * toast.push({\n * title: 'Custom Toast',\n * status: 'info',\n * duration: 3000,\n * description: 'This is a custom toast',\n * position: 'top-right'\n * preventDuplicates: true,\n * })\n */\n push = (params: ToastParams) => {\n if (!this.emit)\n throw new Error(\"Toast not initialized: wrap your app with ToastProvider\")\n return this.emit(params)\n }\n\n /**\n * Handle async operations with loading, success, and error states\n * @param promise - Function that returns a promise\n * @param options - Configuration for different states of the promise\n * @example\n * toast.promise(\n * () => fetchUserData(),\n * {\n * loading: 'Fetching user...',\n * success: (data) => `Welcome ${data.name}!`,\n * error: 'Failed to fetch user'\n * }\n * )\n */\n promise = (promise: () => Promise, options: ToastPromiseOptions) => {\n if (!this.emitPromise)\n throw new Error(\"Toast not initialized: wrap your app with ToastProvider\")\n return this.emitPromise(promise, options)\n }\n}\n\n/**\n * Exported toast instance for use throughout the application\n */\nexport const toast = new Toast()\n", "type": "registry:ui", "target": "components/ui/notify/notify-utils.ts" }, { "path": "packages/notify/src/index.ts", "content": "export * from \"./notify-provider\"\n\nexport type * from \"./notify-types\"\n\nexport { toast } from \"./notify-utils\"\n\nexport { Toast } from \"./notify\"\n\nexport {\n progressBarVariants,\n statusStyles,\n toastActionVariants,\n toastPositionVariants,\n toastTypeVariants,\n toastVariants,\n} from \"./notify-variants\"\n", "type": "registry:ui", "target": "components/ui/notify/index.ts" }, { "path": "packages/notify/src/notify-state-manager.ts", "content": "\"use client\"\n\nimport { useCallback, useEffect, useRef, useState } from \"react\"\n\nimport type {\n AnimationType,\n ToastParams,\n ToastPromiseOptions,\n ToastProviderProps,\n ToastState,\n ToastTimeoutRef,\n} from \"./notify-types\"\nimport { generateId } from \"./notify-utils\"\n\n/**\n * Custom hook for managing toast state and notifications\n * Provides functions for creating, updating, and dismissing toast notifications\n *\n * @param config Configuration options for toast state management\n * @returns Toast state manager with functions for toast operations\n */\nexport function useToastStateManager({\n position: defaultPosition = \"bottom-right\",\n duration: defaultDuration = 4000,\n classNames: defaultClassNames = {},\n closable: defaultClosable = true,\n preventDuplicates: defaultPreventDuplicates = false,\n maxToast: defaultMaxToast = 4,\n hideProgressBar: defaultHideProgressBar = false,\n animation: defaultAnimation = \"slide\",\n}: Omit) {\n const [toasts, setToasts] = useState([])\n const toastsRef = useRef>({})\n\n // Toast dismissal function\n const dismiss = useCallback((id: string | number) => {\n setToasts((prev) => prev.filter((t) => t.id !== String(id)))\n const toast = toastsRef.current[String(id)]\n if (toast) {\n clearTimeout(toast.timeout)\n if (toast.onClose) {\n toast.onClose()\n }\n delete toastsRef.current[String(id)]\n }\n }, [])\n\n // Create a function to generate a dismiss handler for a specific toast ID\n const createDismiss = useCallback(\n (id: string) => () => dismiss(id),\n [dismiss]\n )\n\n // Push a new toast notification\n const push = useCallback(\n (params: ToastParams) => {\n const id = params.id ?? generateId()\n const toastDuration = params.duration ?? defaultDuration\n\n const dismissToast = createDismiss(id)\n\n // Update the toast state using a functional update to prevent stale state\n setToasts((prevToasts) => {\n // Handle duplicate prevention\n if (params.preventDuplicates ?? defaultPreventDuplicates) {\n const isDuplicate = prevToasts.some(\n (toast) =>\n toast.params.text === params.text &&\n toast.params.status === params.status\n )\n if (isDuplicate) {\n return prevToasts\n }\n }\n\n const filteredToasts = prevToasts.filter((t) => t.id !== id)\n const newToast: ToastState = {\n dismiss: dismissToast,\n id,\n params: {\n ...params,\n duration: toastDuration,\n closable: params.closable ?? defaultClosable,\n hideProgressBar: params.hideProgressBar ?? defaultHideProgressBar,\n classNames: params.classNames ?? defaultClassNames,\n position: params.position ?? defaultPosition,\n animation: (params.animation ?? defaultAnimation) as AnimationType,\n },\n paused: false,\n }\n\n const updatedToasts = [newToast, ...filteredToasts]\n return updatedToasts.slice(0, params.maxToast ?? defaultMaxToast)\n })\n\n // Clear any existing timeout for this toast ID\n if (toastsRef.current[id]) {\n clearTimeout(toastsRef.current[id].timeout)\n }\n\n // Set a new timeout if duration is finite\n if (toastDuration !== Infinity) {\n toastsRef.current[id] = {\n timeout: setTimeout(dismissToast, toastDuration),\n remaining: toastDuration,\n startTime: Date.now(),\n onClose: params.onClose,\n }\n }\n\n return id\n },\n [\n defaultPreventDuplicates,\n defaultDuration,\n defaultClosable,\n defaultHideProgressBar,\n defaultClassNames,\n defaultPosition,\n defaultMaxToast,\n defaultAnimation,\n createDismiss,\n ]\n )\n\n // Handle promise-based toasts\n const pushPromise = useCallback(\n (promise: () => Promise, options: ToastPromiseOptions) => {\n const id = generateId()\n const dismiss = createDismiss(id)\n const position = options.position\n const toastDuration = options.duration ?? defaultDuration\n const toastClassNames = options.classNames ?? defaultClassNames\n const toastAnimation = options.animation ?? defaultAnimation\n\n // Show loading toast\n push({\n status: \"loading\",\n text: options.loading,\n id,\n position,\n classNames: toastClassNames,\n duration: toastDuration,\n animation: toastAnimation,\n })\n\n // Handle promise resolution and rejection\n promise()\n .then((data) => {\n // Clear existing timeout\n if (toastsRef.current[id]) {\n clearTimeout(toastsRef.current[id].timeout)\n }\n\n // Update with success state\n setToasts((prev) => [\n {\n dismiss,\n id,\n params: {\n status: \"success\",\n text: options.success(data),\n duration: toastDuration,\n closable: true,\n classNames: toastClassNames,\n position,\n hideProgressBar: defaultHideProgressBar,\n animation: toastAnimation,\n },\n paused: false,\n },\n ...prev.filter((t) => t.id !== id),\n ])\n\n // Set new timeout\n if (toastDuration !== Infinity) {\n toastsRef.current[id] = {\n timeout: setTimeout(dismiss, toastDuration),\n remaining: toastDuration,\n startTime: Date.now(),\n }\n }\n })\n .catch(() => {\n // Clear existing timeout\n if (toastsRef.current[id]) {\n clearTimeout(toastsRef.current[id].timeout)\n }\n\n // Update with error state\n setToasts((prev) => [\n {\n dismiss,\n id,\n params: {\n status: \"error\",\n text: options.error ?? \"Error\",\n duration: toastDuration,\n classNames: toastClassNames,\n position,\n hideProgressBar: defaultHideProgressBar,\n animation: toastAnimation,\n },\n paused: false,\n },\n ...prev.filter((t) => t.id !== id),\n ])\n\n // Set new timeout\n if (toastDuration !== Infinity) {\n toastsRef.current[id] = {\n timeout: setTimeout(dismiss, toastDuration),\n remaining: toastDuration,\n startTime: Date.now(),\n }\n }\n })\n\n return id\n },\n [\n defaultDuration,\n defaultClassNames,\n defaultAnimation,\n defaultHideProgressBar,\n push,\n createDismiss,\n ]\n )\n\n // Pause a toast (e.g., on mouse hover)\n const pauseToast = useCallback((id: string) => {\n if (toastsRef.current[id]) {\n clearTimeout(toastsRef.current[id].timeout)\n const remaining =\n toastsRef.current[id].remaining -\n (Date.now() - toastsRef.current[id].startTime)\n toastsRef.current[id].remaining = remaining\n setToasts((prev) =>\n prev.map((t) => (t.id === id ? { ...t, paused: true } : t))\n )\n }\n }, [])\n\n // Resume a toast\n const resumeToast = useCallback(\n (id: string) => {\n if (toastsRef.current[id]) {\n toastsRef.current[id].startTime = Date.now()\n toastsRef.current[id].timeout = setTimeout(\n () => dismiss(id),\n toastsRef.current[id].remaining\n )\n setToasts((prev) =>\n prev.map((t) => (t.id === id ? { ...t, paused: false } : t))\n )\n }\n },\n [dismiss]\n )\n\n // Cleanup on unmount\n useEffect(() => {\n return () => {\n Object.values(toastsRef.current).forEach((toast) =>\n clearTimeout(toast.timeout)\n )\n toastsRef.current = {}\n }\n }, [])\n\n // Organize toasts by position for efficient rendering\n const toastsByPosition = (() => {\n const positions = {} as Record\n for (const toast of toasts) {\n const position = toast.params.position ?? defaultPosition\n positions[position!] = positions[position!] || []\n positions[position!].push(toast)\n }\n return positions\n })()\n\n return {\n toasts,\n toastsByPosition,\n push,\n pushPromise,\n dismiss,\n pauseToast,\n resumeToast,\n }\n}\n", "type": "registry:ui", "target": "components/ui/notify/notify-state-manager.ts" }, { "path": "packages/notify/src/notify-variants.ts", "content": "\"use client\"\n\nimport { cva } from \"class-variance-authority\"\n\n/**\n * Toast positioning variants for different screen positions\n * Defines positioning classes for each toast location option\n */\nexport const toastPositionVariants = cva(\n \"absolute w-full max-w-[420px] p-4 md:p-8\",\n {\n variants: {\n position: {\n \"top-left\": \"top-0 left-0 md:top-0\",\n \"top-center\": \"top-0 left-1/2 -translate-x-1/2 transform md:top-0\",\n \"top-right\": \"top-0 right-0 md:top-0\",\n \"bottom-left\": \"bottom-0 left-0 md:bottom-0\",\n \"bottom-center\":\n \"bottom-0 left-1/2 -translate-x-1/2 transform md:bottom-0\",\n \"bottom-right\": \"right-0 bottom-0 md:bottom-0\",\n },\n },\n defaultVariants: {\n position: \"bottom-right\",\n },\n }\n)\n\n/**\n * Toast type variants for different notification types\n * Defines style classes for success, error, warning, etc.\n */\nexport const toastTypeVariants = cva(\"toast-base\", {\n variants: {\n type: {\n success: \"toast-success\",\n error: \"toast-error\",\n warning: \"toast-warning\",\n info: \"toast-info\",\n loading: \"toast-loading\",\n default: \"toast-default\",\n },\n },\n defaultVariants: {\n type: \"default\",\n },\n})\n\n/**\n * Status styles for different notification types\n * Defines style classes for success, error, warning, etc.\n */\nexport const statusStyles = {\n error:\n \"dark:bg-[#24161b] bg-[#fff5f5] text-red-900 dark:text-[#ffdfdd] dark:border-red-900 border-red-200\",\n warning:\n \"dark:bg-[#1E1A1B] bg-[#fefae1] text-[#3b2212] dark:text-[#EADB90] dark:border-[#5C431B] border-[#ddcab8]\",\n success:\n \"dark:bg-[#131d1e] bg-[#e7fef6] text-[#0d311e] dark:text-[#abf9de] dark:border-[#1e5643] border-green-200\",\n info: \"dark:bg-[#161831] bg-[#edf4ff] text-[#1e3a8a] dark:text-[#DCE6FF] dark:border-[#2f3873] border-[#bfdbfe]\",\n default:\n \"dark:bg-[#13141b] bg-white text-gray-900 dark:text-[#e4e5e9] dark:border-[#3a3c4a] border-gray-200\",\n loading: \"bg-white text-gray-900 border-gray-200 \",\n} as const\n\n/**\n * Toast variants for different notification types\n * Defines style classes for success, error, warning, etc.\n */\nexport const toastVariants = cva(\n \"relative flex w-full flex-col gap-1 overflow-hidden rounded-lg border p-[0.75rem] shadow-lg\",\n {\n variants: {\n status: statusStyles,\n },\n defaultVariants: {\n status: \"default\",\n },\n }\n)\n\n/**\n * Toast progress bar variants for different notification types\n * Defines style classes for success, error, warning, etc.\n */\nexport const progressBarVariants = cva(\"absolute bottom-0 left-0 h-[2px]\", {\n variants: {\n status: {\n error: \"bg-red-600 dark:bg-[#f77a6f]\",\n warning: \"bg-yellow-500 dark:bg-[#fabe20]\",\n success: \"bg-green-600 dark:bg-[#12f0a5]\",\n info: \"bg-blue-600 dark:bg-[#7898ff]\",\n default: \"bg-gray-600 dark:bg-[#e4e5e9]\",\n },\n },\n defaultVariants: {\n status: \"default\",\n },\n})\n\n/**\n * Toast action variants for different notification types\n * Defines style classes for success, error, warning, etc.\n */\nexport const toastActionVariants = cva(\n \"inline-flex items-center justify-center rounded-md px-3 py-1.5 text-sm font-medium transition-colors focus:ring-2 focus:ring-ring focus:ring-offset-2 focus:outline-hidden\",\n {\n variants: {\n variant: {\n primary:\n \"bg-white text-black hover:bg-white/90 dark:text-black dark:hover:bg-white/90\",\n dismiss: \"bg-transparent hover:bg-black/10 dark:hover:bg-white/10\",\n },\n },\n defaultVariants: {\n variant: \"primary\",\n },\n }\n)\n", "type": "registry:ui", "target": "components/ui/notify/notify-variants.ts" }, { "path": "packages/notify/src/notify-config.ts", "content": "/**\n * Current version of the notify module\n */\nexport const NOTIFY_VERSION = \"1.4.0\"\n\n// visit https://reusables.vercel.app/docs/components/notify for more information\n\n/**\n * Default configuration for toast notifications\n */\nexport const DEFAULT_CONFIG = {\n duration: 4000,\n position: \"bottom-right\",\n closable: true,\n preventDuplicates: false,\n maxToast: 4,\n hideProgressBar: false,\n animation: \"slide\",\n} as const\n", "type": "registry:ui", "target": "components/ui/notify/notify-config.ts" }, { "path": "packages/notify/src/notify-animations.ts", "content": "\"use client\"\n\nimport type { AnimationType, ToastPosition } from \"./notify-types\"\n\nexport function getAnimationProps(\n animationType: AnimationType,\n position: ToastPosition\n) {\n const isTop = position === \"top-left\" || position === \"top-right\"\n const isRight = position === \"top-right\" || position === \"bottom-right\"\n switch (animationType) {\n case \"slide\":\n return {\n initial: { opacity: 0, x: isRight ? 100 : -100 },\n animate: { opacity: 1, x: 0 },\n exit: { opacity: 0, x: isRight ? 100 : -100 },\n transition: { type: \"spring\" as const, stiffness: 300, damping: 30 },\n }\n case \"fade\":\n return {\n initial: { opacity: 0 },\n animate: { opacity: 1 },\n exit: { opacity: 0 },\n transition: { type: \"spring\" as const, stiffness: 300, damping: 30 },\n }\n case \"scale\":\n return {\n initial: { opacity: 0, scale: 0.85 },\n animate: { opacity: 1, scale: 1 },\n exit: { opacity: 0, scale: 0.85 },\n transition: { type: \"spring\" as const, stiffness: 300, damping: 30 },\n }\n case \"bounce\":\n return {\n initial: { opacity: 0, scale: 0.5 },\n animate: { opacity: 1, scale: 1 },\n exit: { opacity: 0, scale: 0.5 },\n transition: { type: \"spring\" as const, stiffness: 400, damping: 25 },\n }\n default:\n return {\n initial: { opacity: 0, y: isTop ? -80 : 80 },\n animate: { opacity: 1, y: 0 },\n exit: { opacity: 0, y: isTop ? -80 : 80 },\n transition: { type: \"spring\" as const, stiffness: 300, damping: 30 },\n }\n }\n}\n", "type": "registry:ui", "target": "components/ui/notify/notify-animations.ts" }, { "path": "packages/notify/src/notify-types.ts", "content": "\"use client\"\n\nimport React from \"react\"\nimport { VariantProps } from \"class-variance-authority\"\n\nimport { toastPositionVariants } from \"./notify-variants\"\n\n/**\n * Available animation types for toast notifications\n */\nexport type AnimationType = \"slide\" | \"fade\" | \"scale\" | \"bounce\"\n\n/**\n * Toast position variants derived from the toast position animations\n */\nexport type ToastPosition = VariantProps<\n typeof toastPositionVariants\n>[\"position\"]\n\n/**\n * Action configuration for interactive toast elements\n */\nexport interface Action {\n /** Text label for the action */\n label: string\n /** Click handler function */\n onClick: () => void\n /** Visual style variant */\n variant?: \"button\" | \"icon\"\n /** Optional icon to display */\n icon?: React.ReactNode\n}\n\n/**\n * Parameters for configuring a toast notification\n */\nexport interface ToastParams {\n /** Toast title content */\n title?: React.ReactNode\n /** Main toast message content */\n text?: React.ReactNode\n /** Additional descriptive content */\n description?: React.ReactNode\n /** Duration in milliseconds before auto-dismissal */\n duration?: number\n /** Unique identifier for the toast */\n id?: string\n /** Callback executed when toast is closed */\n onClose?: () => void\n /** Visual status type of the toast */\n status?: \"error\" | \"warning\" | \"success\" | \"info\" | \"default\" | \"loading\"\n /** Visual variant for loading indicator */\n loaderVariant?: \"loader-1\" | \"loader-2\"\n /** Screen position for the toast */\n position?: ToastPosition\n /** Whether the toast can be manually closed */\n closable?: boolean\n /** Whether to hide the progress bar */\n hideProgressBar?: boolean\n /** Whether to prevent duplicate toasts with the same content */\n preventDuplicates?: boolean\n /** Maximum number of toasts to show simultaneously */\n maxToast?: number\n /** Interactive action buttons configuration */\n actions?: {\n primary: Action\n dismiss?: Omit & { onClick?: () => void }\n }\n /** Custom CSS class names for toast elements */\n classNames?: ToastClassNames\n /** Animation style for entry/exit */\n animation?: AnimationType\n}\n\n/**\n * CSS class names configuration for toast components\n */\nexport interface ToastClassNames {\n /** Class for error toast variant */\n error?: string\n /** Class for success toast variant */\n success?: string\n /** Class for warning toast variant */\n warning?: string\n /** Class for info toast variant */\n info?: string\n /** Class for loading toast variant */\n loading?: string\n /** Class for close button */\n closeButton?: string\n /** Class for toast title */\n title?: string\n /** Class for toast description */\n description?: string\n /** Class for loading indicator */\n loader?: string\n /** Class for progress bar */\n progressBar?: string\n /** Class for container element */\n containerClassName?: string\n}\n\n/**\n * Options for promise-based toast notifications\n */\nexport interface ToastPromiseOptions {\n /** Message to show during loading state */\n loading: string\n /** Function to generate success message from resolved data */\n success: (data: T) => string\n /** Message to show on promise rejection */\n error?: string\n /** Screen position for the toast */\n position?: ToastPosition\n /** Duration in milliseconds before auto-dismissal */\n duration?: number\n /** Custom CSS class names */\n classNames?: ToastClassNames\n /** Animation style for entry/exit */\n animation?: AnimationType\n}\n\n/**\n * Props for the ToastProvider component\n */\nexport interface ToastProviderProps {\n /** Child components */\n children: React.ReactNode\n /** Default duration in milliseconds before auto-dismissal */\n duration?: number\n /** Default screen position for toasts */\n position?: ToastPosition\n /** Default CSS class names */\n classNames?: ToastClassNames\n /** Whether toasts are closable by default */\n closable?: boolean\n /** Whether to prevent duplicate toasts by default */\n preventDuplicates?: boolean\n /** Default maximum number of toasts to show simultaneously */\n maxToast?: number\n /** Whether to hide progress bars by default */\n hideProgressBar?: boolean\n /** Default animation style */\n animation?: AnimationType\n}\n\n/**\n * Function type for toast creation methods\n */\nexport type ToastFunction = (\n text: React.ReactNode,\n options?: Partial\n) => string\n\n/**\n * Function type for promise-based toast handlers\n */\nexport type PromiseHandler = (\n promise: () => Promise,\n options: ToastPromiseOptions\n) => string\n\n/**\n * Interface for toast API methods\n */\nexport interface ToastMethods {\n /** Display a success toast */\n success: ToastFunction\n /** Display an error toast */\n error: ToastFunction\n /** Display a warning toast */\n warning: ToastFunction\n /** Display an info toast */\n info: ToastFunction\n /** Display a loading toast */\n loading: ToastFunction\n /** Display a default toast */\n default: ToastFunction\n /** Create a custom toast */\n push: (params: ToastParams) => string\n /** Create a promise-based toast */\n promise: PromiseHandler\n /** Dismiss a toast by ID */\n dismiss: (id: string | number) => void\n}\n\n/**\n * Interface for toast state\n */\nexport interface ToastState {\n /** Function to dismiss this toast */\n dismiss: () => void\n /** Unique identifier for the toast */\n id: string\n /** Configuration parameters */\n params: ToastParams\n /** Whether toast is currently paused (e.g., during hover) */\n paused: boolean\n}\n\n/**\n * Interface for toast state manager\n */\nexport interface ToastStateManager {\n /** All active toasts */\n toasts: ToastState[]\n /** Toasts organized by position */\n toastsByPosition: Record\n /** Create a new toast */\n push: (params: ToastParams) => string\n /** Create a promise-based toast */\n pushPromise: (\n promise: () => Promise,\n options: ToastPromiseOptions\n ) => string\n /** Dismiss a toast by ID */\n dismiss: (id: string | number) => void\n /** Pause a toast's timeout */\n pauseToast: (id: string) => void\n /** Resume a toast's timeout */\n resumeToast: (id: string) => void\n}\n\n/**\n * Interface for managing toast timeout references\n */\nexport interface ToastTimeoutRef {\n timeout: NodeJS.Timeout\n remaining: number\n startTime: number\n onClose?: () => void\n}\n", "type": "registry:ui", "target": "components/ui/notify/notify-types.ts" }, { "path": "packages/notify/src/cn.ts", "content": "import { clsx, type ClassValue } from \"clsx\"\nimport { twMerge } from \"tailwind-merge\"\n\nexport function cn(...inputs: ClassValue[]) {\n return twMerge(clsx(inputs))\n}\n", "type": "registry:ui", "target": "components/ui/notify/cn.ts" } ], "type": "registry:ui" }