{
  "name": "toast",
  "dependencies": [],
  "registryDependencies": [
    "utils"
  ],
  "files": [
    {
      "name": "Toast.vue",
      "content": "<script setup lang=\"ts\">\nimport { computed } from 'vue'\nimport { ToastRoot, type ToastRootEmits, useForwardPropsEmits } from 'radix-vue'\nimport { type ToastProps, toastVariants } from '.'\nimport { cn } from '@/lib/utils'\n\nconst props = defineProps<ToastProps>()\n\nconst emits = defineEmits<ToastRootEmits>()\n\nconst delegatedProps = computed(() => {\n  const { class: _, ...delegated } = props\n\n  return delegated\n})\n\nconst forwarded = useForwardPropsEmits(delegatedProps, emits)\n</script>\n\n<template>\n  <ToastRoot\n    v-bind=\"forwarded\"\n    :class=\"cn(toastVariants({ variant }), props.class)\"\n    @update:open=\"onOpenChange\"\n  >\n    <slot />\n  </ToastRoot>\n</template>\n"
    },
    {
      "name": "ToastAction.vue",
      "content": "<script setup lang=\"ts\">\nimport { type HTMLAttributes, computed } from 'vue'\nimport { ToastAction, type ToastActionProps } from 'radix-vue'\nimport { cn } from '@/lib/utils'\n\nconst props = defineProps<ToastActionProps & { class?: HTMLAttributes['class'] }>()\n\nconst delegatedProps = computed(() => {\n  const { class: _, ...delegated } = props\n\n  return delegated\n})\n</script>\n\n<template>\n  <ToastAction\n    v-bind=\"delegatedProps\"\n    :class=\"cn('inline-flex h-8 shrink-0 items-center justify-center rounded-md border bg-transparent px-3 text-sm font-medium ring-offset-background transition-colors hover:bg-secondary focus:outline-none focus:ring-2 focus:ring-ring focus:ring-offset-2 disabled:pointer-events-none disabled:opacity-50 group-[.destructive]:border-muted/40 group-[.destructive]:hover:border-destructive/30 group-[.destructive]:hover:bg-destructive group-[.destructive]:hover:text-destructive-foreground group-[.destructive]:focus:ring-destructive', props.class)\"\n  >\n    <slot />\n  </ToastAction>\n</template>\n"
    },
    {
      "name": "ToastClose.vue",
      "content": "<script setup lang=\"ts\">\nimport { type HTMLAttributes, computed } from 'vue'\nimport { ToastClose, type ToastCloseProps } from 'radix-vue'\nimport { XIcon } from 'lucide-vue-next'\nimport { cn } from '@/lib/utils'\n\nconst props = defineProps<ToastCloseProps & {\n  class?: HTMLAttributes['class']\n}>()\n\nconst delegatedProps = computed(() => {\n  const { class: _, ...delegated } = props\n\n  return delegated\n})\n</script>\n\n<template>\n  <ToastClose\n    v-bind=\"delegatedProps\"\n    :class=\"cn('absolute right-2 top-2 rounded-md p-1 text-foreground/50 opacity-0 transition-opacity hover:text-foreground focus:opacity-100 focus:outline-none focus:ring-2 group-hover:opacity-100 group-[.destructive]:text-red-300 group-[.destructive]:hover:text-red-50 group-[.destructive]:focus:ring-red-400 group-[.destructive]:focus:ring-offset-red-600', props.class)\"\n  >\n    <XIcon class=\"h-4 w-4\" />\n  </ToastClose>\n</template>\n"
    },
    {
      "name": "ToastDescription.vue",
      "content": "<script setup lang=\"ts\">\nimport { type HTMLAttributes, computed } from 'vue'\nimport { ToastDescription, type ToastDescriptionProps } from 'radix-vue'\nimport { cn } from '@/lib/utils'\n\nconst props = defineProps<ToastDescriptionProps & { class?: HTMLAttributes['class'] }>()\n\nconst delegatedProps = computed(() => {\n  const { class: _, ...delegated } = props\n\n  return delegated\n})\n</script>\n\n<template>\n  <ToastDescription\n    :class=\"cn('text-sm opacity-90', props.class)\"\n    v-bind=\"delegatedProps\"\n  >\n    <slot />\n  </ToastDescription>\n</template>\n"
    },
    {
      "name": "ToastProvider.vue",
      "content": "<script setup lang=\"ts\">\nimport { ToastProvider, type ToastProviderProps } from 'radix-vue'\n\nconst props = defineProps<ToastProviderProps>()\n</script>\n\n<template>\n  <ToastProvider v-bind=\"props\">\n    <slot />\n  </ToastProvider>\n</template>\n"
    },
    {
      "name": "ToastTitle.vue",
      "content": "<script setup lang=\"ts\">\nimport { type HTMLAttributes, computed } from 'vue'\nimport { ToastTitle, type ToastTitleProps } from 'radix-vue'\nimport { cn } from '@/lib/utils'\n\nconst props = defineProps<ToastTitleProps & { class?: HTMLAttributes['class'] }>()\n\nconst delegatedProps = computed(() => {\n  const { class: _, ...delegated } = props\n\n  return delegated\n})\n</script>\n\n<template>\n  <ToastTitle\n    v-bind=\"delegatedProps\"\n    :class=\"cn('text-sm font-semibold', props.class)\"\n  >\n    <slot />\n  </ToastTitle>\n</template>\n"
    },
    {
      "name": "ToastViewport.vue",
      "content": "<script setup lang=\"ts\">\nimport { type HTMLAttributes, computed } from 'vue'\nimport { ToastViewport, type ToastViewportProps } from 'radix-vue'\nimport { cn } from '@/lib/utils'\n\nconst props = defineProps<ToastViewportProps & { class?: HTMLAttributes['class'] }>()\n\nconst delegatedProps = computed(() => {\n  const { class: _, ...delegated } = props\n\n  return delegated\n})\n</script>\n\n<template>\n  <ToastViewport\n    v-bind=\"delegatedProps\"\n    :class=\"cn('fixed top-0 z-[100] flex max-h-screen w-full flex-col-reverse p-4 sm:bottom-0 sm:right-0 sm:top-auto sm:flex-col md:max-w-[420px]', props.class)\"\n  />\n</template>\n"
    },
    {
      "name": "Toaster.vue",
      "content": "<script setup lang=\"ts\">\nimport { isVNode } from 'vue'\nimport { useToast } from './use-toast'\nimport { Toast, ToastClose, ToastDescription, ToastProvider, ToastTitle, ToastViewport } from '.'\n\nconst { toasts } = useToast()\n</script>\n\n<template>\n  <ToastProvider>\n    <Toast\n      v-for=\"toast in toasts\"\n      :key=\"toast.id\"\n      v-bind=\"toast\"\n    >\n      <div class=\"grid gap-1\">\n        <ToastTitle v-if=\"toast.title\">\n          {{ toast.title }}\n        </ToastTitle>\n        <template v-if=\"toast.description\">\n          <ToastDescription v-if=\"isVNode(toast.description)\">\n            <component :is=\"toast.description\" />\n          </ToastDescription>\n          <ToastDescription v-else>\n            {{ toast.description }}\n          </ToastDescription>\n        </template>\n        <ToastClose />\n      </div>\n      <component :is=\"toast.action\" />\n    </Toast>\n    <ToastViewport />\n  </ToastProvider>\n</template>\n"
    },
    {
      "name": "index.ts",
      "content": "import type { ToastRootProps } from 'radix-vue'\nimport type { HTMLAttributes } from 'vue'\n\nexport { default as Toaster } from './Toaster.vue'\nexport { default as Toast } from './Toast.vue'\nexport { default as ToastViewport } from './ToastViewport.vue'\nexport { default as ToastAction } from './ToastAction.vue'\nexport { default as ToastClose } from './ToastClose.vue'\nexport { default as ToastTitle } from './ToastTitle.vue'\nexport { default as ToastDescription } from './ToastDescription.vue'\nexport { default as ToastProvider } from './ToastProvider.vue'\nexport { toast, useToast } from './use-toast'\n\nimport { type VariantProps, cva } from 'class-variance-authority'\n\nexport const toastVariants = cva(\n  'group pointer-events-auto relative flex w-full items-center justify-between space-x-4 overflow-hidden rounded-md border p-6 pr-8 shadow-lg transition-all data-[swipe=cancel]:translate-x-0 data-[swipe=end]:translate-x-[--radix-toast-swipe-end-x] data-[swipe=move]:translate-x-[--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',\n  {\n    variants: {\n      variant: {\n        default: 'border bg-background text-foreground',\n        destructive:\n                    'destructive group border-destructive bg-destructive text-destructive-foreground',\n      },\n    },\n    defaultVariants: {\n      variant: 'default',\n    },\n  },\n)\n\ntype ToastVariants = VariantProps<typeof toastVariants>\n\nexport interface ToastProps extends ToastRootProps {\n  class?: HTMLAttributes['class']\n  variant?: ToastVariants['variant']\n  onOpenChange?: ((value: boolean) => void) | undefined\n}\n"
    },
    {
      "name": "use-toast.ts",
      "content": "import { computed, ref } from 'vue'\nimport type { Component, VNode } from 'vue'\nimport type { ToastProps } from '.'\n\nconst TOAST_LIMIT = 1\nconst TOAST_REMOVE_DELAY = 1000000\n\nexport type StringOrVNode =\n  | string\n  | VNode\n  | (() => VNode)\n\ntype ToasterToast = ToastProps & {\n  id: string\n  title?: string\n  description?: StringOrVNode\n  action?: Component\n}\n\nconst actionTypes = {\n  ADD_TOAST: 'ADD_TOAST',\n  UPDATE_TOAST: 'UPDATE_TOAST',\n  DISMISS_TOAST: 'DISMISS_TOAST',\n  REMOVE_TOAST: 'REMOVE_TOAST',\n} as const\n\nlet count = 0\n\nfunction genId() {\n  count = (count + 1) % Number.MAX_VALUE\n  return count.toString()\n}\n\ntype ActionType = typeof actionTypes\n\ntype Action =\n  | {\n    type: ActionType['ADD_TOAST']\n    toast: ToasterToast\n  }\n  | {\n    type: ActionType['UPDATE_TOAST']\n    toast: Partial<ToasterToast>\n  }\n  | {\n    type: ActionType['DISMISS_TOAST']\n    toastId?: ToasterToast['id']\n  }\n  | {\n    type: ActionType['REMOVE_TOAST']\n    toastId?: ToasterToast['id']\n  }\n\ninterface State {\n  toasts: ToasterToast[]\n}\n\nconst toastTimeouts = new Map<string, ReturnType<typeof setTimeout>>()\n\nfunction addToRemoveQueue(toastId: string) {\n  if (toastTimeouts.has(toastId))\n    return\n\n  const timeout = setTimeout(() => {\n    toastTimeouts.delete(toastId)\n    dispatch({\n      type: actionTypes.REMOVE_TOAST,\n      toastId,\n    })\n  }, TOAST_REMOVE_DELAY)\n\n  toastTimeouts.set(toastId, timeout)\n}\n\nconst state = ref<State>({\n  toasts: [],\n})\n\nfunction dispatch(action: Action) {\n  switch (action.type) {\n    case actionTypes.ADD_TOAST:\n      state.value.toasts = [action.toast, ...state.value.toasts].slice(0, TOAST_LIMIT)\n      break\n\n    case actionTypes.UPDATE_TOAST:\n      state.value.toasts = state.value.toasts.map(t =>\n        t.id === action.toast.id ? { ...t, ...action.toast } : t,\n      )\n      break\n\n    case actionTypes.DISMISS_TOAST: {\n      const { toastId } = action\n\n      if (toastId) {\n        addToRemoveQueue(toastId)\n      }\n      else {\n        state.value.toasts.forEach((toast) => {\n          addToRemoveQueue(toast.id)\n        })\n      }\n\n      state.value.toasts = state.value.toasts.map(t =>\n        t.id === toastId || toastId === undefined\n          ? {\n              ...t,\n              open: false,\n            }\n          : t,\n      )\n      break\n    }\n\n    case actionTypes.REMOVE_TOAST:\n      if (action.toastId === undefined)\n        state.value.toasts = []\n      else\n        state.value.toasts = state.value.toasts.filter(t => t.id !== action.toastId)\n\n      break\n  }\n}\n\nfunction useToast() {\n  return {\n    toasts: computed(() => state.value.toasts),\n    toast,\n    dismiss: (toastId?: string) => dispatch({ type: actionTypes.DISMISS_TOAST, toastId }),\n  }\n}\n\ntype Toast = Omit<ToasterToast, 'id'>\n\nfunction toast(props: Toast) {\n  const id = genId()\n\n  const update = (props: ToasterToast) =>\n    dispatch({\n      type: actionTypes.UPDATE_TOAST,\n      toast: { ...props, id },\n    })\n\n  const dismiss = () => dispatch({ type: actionTypes.DISMISS_TOAST, toastId: id })\n\n  dispatch({\n    type: actionTypes.ADD_TOAST,\n    toast: {\n      ...props,\n      id,\n      open: true,\n      onOpenChange: (open: boolean) => {\n        if (!open)\n          dismiss()\n      },\n    },\n  })\n\n  return {\n    id,\n    dismiss,\n    update,\n  }\n}\n\nexport { toast, useToast }\n"
    }
  ],
  "type": "components:ui"
}