{"$schema":"https://ui.shadcn.com/schema/registry-item.json","name":"attachment","title":"Attachment","description":"An uploaded or in-flight file as a picture, a card that names its type or a row (full or compact) with its icon, name and metadata, with its upload state and actions.","dependencies":["cn","@base-ui/react","class-variance-authority"],"registryDependencies":["@sortui/button"],"files":[{"path":"components/ui/attachment.tsx","content":"import * as React from \"react\"\nimport { mergeProps } from \"@base-ui/react/merge-props\"\nimport { useRender } from \"@base-ui/react/use-render\"\nimport { cva, type VariantProps } from \"class-variance-authority\"\nimport { cn } from \"cn\"\n\nimport { Button } from \"@/components/ui/button\"\n\nconst revealActions =\n  \"*:data-[slot=attachment-actions]:transition-opacity [@media(hover:hover)]:*:data-[slot=attachment-actions]:opacity-0 hover:*:data-[slot=attachment-actions]:opacity-100 has-[:focus-visible]:*:data-[slot=attachment-actions]:opacity-100 data-[state=error]:*:data-[slot=attachment-actions]:opacity-100\"\n\nconst attachmentVariants = cva(\n  \"group/attachment relative flex min-w-0 rounded-xl bg-popover text-sm text-popover-foreground shadow-[0_1px_1px_-1px_oklch(0.371_0_0/6%),0_2px_2px_-1px_oklch(0.371_0_0/3%)] transition-colors after:pointer-events-none after:absolute after:inset-0 after:rounded-[inherit] after:inset-ring-1 after:inset-ring-border has-[:focus-visible]:ring-3 has-[:focus-visible]:ring-ring/30 data-[state=error]:after:inset-ring-destructive/30\",\n  {\n    variants: {\n      type: {\n        file: \"w-full items-center gap-2.5 px-3 py-2 hover:bg-control-hover\",\n        card: `size-40 flex-col hover:bg-control-hover ${revealActions}`,\n        image: `size-40 flex-col not-data-[cover=true]:hover:bg-control-hover ${revealActions}`,\n      },\n      variant: {\n        default: \"\",\n        muted: \"\",\n      },\n      size: {\n        default: \"\",\n        sm: \"\",\n        xs: \"\",\n      },\n      orientation: {\n        horizontal: \"\",\n        vertical: \"\",\n      },\n    },\n    compoundVariants: [\n      {\n        type: \"file\",\n        size: [\"sm\", \"xs\"],\n        className: \"gap-1.5 rounded-lg px-2 py-1.5\",\n      },\n    ],\n    defaultVariants: {\n      type: \"file\",\n      variant: \"default\",\n      size: \"default\",\n      orientation: \"horizontal\",\n    },\n  }\n)\n\nfunction Attachment({\n  className,\n  state = \"done\",\n  variant = \"default\",\n  size = \"default\",\n  orientation = \"horizontal\",\n  type,\n  ...props\n}: Omit<React.ComponentProps<\"div\">, \"type\"> &\n  VariantProps<typeof attachmentVariants> & {\n    state?: \"idle\" | \"uploading\" | \"processing\" | \"error\" | \"done\"\n  }) {\n  const resolvedType = type ?? (orientation === \"vertical\" ? \"card\" : \"file\")\n  const cover =\n    resolvedType === \"image\" &&\n    state !== \"uploading\" &&\n    state !== \"processing\" &&\n    state !== \"error\"\n  return (\n    <div\n      data-slot=\"attachment\"\n      data-state={state}\n      data-variant={variant}\n      data-size={size}\n      data-type={resolvedType}\n      data-cover={cover || undefined}\n      data-orientation={resolvedType === \"file\" ? \"horizontal\" : \"vertical\"}\n      className={cn(\n        attachmentVariants({ type: resolvedType, variant, size, orientation }),\n        className\n      )}\n      {...props}\n    />\n  )\n}\n\nconst attachmentMediaVariants = cva(\n  \"relative flex shrink-0 items-center justify-center overflow-hidden text-muted-foreground group-data-[type=card]/attachment:block group-data-[type=card]/attachment:justify-start group-data-[type=card]/attachment:truncate group-data-[type=card]/attachment:pt-3 group-data-[type=card]/attachment:pr-10 group-data-[type=card]/attachment:pl-3 group-data-[type=card]/attachment:font-mono group-data-[type=card]/attachment:text-2xs group-data-[type=file]/attachment:size-8 group-data-[size=sm]/attachment:group-data-[type=file]/attachment:size-6 group-data-[size=xs]/attachment:group-data-[type=file]/attachment:size-6 group-data-[type=image]/attachment:absolute group-data-[type=image]/attachment:inset-0 group-data-[type=image]/attachment:rounded-[inherit] group-data-[type=image]/attachment:bg-fill-1 group-data-[type=image]/attachment:not-group-data-[cover=true]/attachment:hidden [&_svg]:pointer-events-none group-data-[type=file]/attachment:[&>[data-slot=file-icon]]:size-8 group-data-[size=sm]/attachment:group-data-[type=file]/attachment:[&>[data-slot=file-icon]]:size-5 group-data-[size=xs]/attachment:group-data-[type=file]/attachment:[&>[data-slot=file-icon]]:size-5 group-data-[type=card]/attachment:[&>svg]:size-4 group-data-[type=file]/attachment:[&>svg]:size-5 group-data-[size=sm]/attachment:group-data-[type=file]/attachment:[&>svg]:size-4 group-data-[size=xs]/attachment:group-data-[type=file]/attachment:[&>svg]:size-4\",\n  {\n    variants: {\n      variant: {\n        icon: \"\",\n        image:\n          \"group-data-[type=file]/attachment:rounded-md group-data-[size=sm]/attachment:group-data-[type=file]/attachment:rounded-sm group-data-[size=xs]/attachment:group-data-[type=file]/attachment:rounded-sm *:[img]:size-full *:[img]:object-cover\",\n      },\n    },\n    defaultVariants: {\n      variant: \"icon\",\n    },\n  }\n)\n\nfunction AttachmentMedia({\n  className,\n  variant = \"icon\",\n  ...props\n}: React.ComponentProps<\"div\"> & VariantProps<typeof attachmentMediaVariants>) {\n  return (\n    <div\n      data-slot=\"attachment-media\"\n      data-variant={variant}\n      className={cn(attachmentMediaVariants({ variant }), className)}\n      {...props}\n    />\n  )\n}\n\nfunction AttachmentContent({\n  className,\n  ...props\n}: React.ComponentProps<\"div\">) {\n  return (\n    <div\n      data-slot=\"attachment-content\"\n      className={cn(\n        \"flex max-w-full min-w-0 flex-1 flex-col gap-0.5 group-data-[cover=true]/attachment:absolute group-data-[cover=true]/attachment:inset-0 group-data-[cover=true]/attachment:justify-end group-data-[cover=true]/attachment:rounded-[inherit] group-data-[cover=true]/attachment:bg-linear-to-b group-data-[cover=true]/attachment:from-transparent group-data-[cover=true]/attachment:to-black/40 group-data-[cover=true]/attachment:text-white group-data-[cover=true]/attachment:transition-colors group-data-[cover=true]/attachment:group-hover/attachment:bg-black/20 group-data-[orientation=vertical]/attachment:mt-auto group-data-[orientation=vertical]/attachment:w-full group-data-[orientation=vertical]/attachment:flex-none group-data-[orientation=vertical]/attachment:p-3\",\n        className\n      )}\n      {...props}\n    />\n  )\n}\n\nfunction AttachmentTitle({\n  className,\n  ...props\n}: React.ComponentProps<\"span\">) {\n  return (\n    <span\n      data-slot=\"attachment-title\"\n      className={cn(\"block max-w-full min-w-0 truncate\", className)}\n      {...props}\n    />\n  )\n}\n\nfunction AttachmentDescription({\n  className,\n  ...props\n}: React.ComponentProps<\"span\">) {\n  return (\n    <span\n      data-slot=\"attachment-description\"\n      className={cn(\n        \"block max-w-full min-w-0 truncate text-2xs text-muted-foreground group-data-[cover=true]/attachment:text-white/70 group-data-[state=error]/attachment:text-destructive-foreground group-data-[state=processing]/attachment:shimmer group-data-[state=uploading]/attachment:shimmer\",\n        className\n      )}\n      {...props}\n    />\n  )\n}\n\nfunction AttachmentActions({\n  className,\n  ...props\n}: React.ComponentProps<\"div\">) {\n  return (\n    <div\n      data-slot=\"attachment-actions\"\n      className={cn(\n        \"relative z-20 flex shrink-0 items-center gap-1 group-data-[orientation=vertical]/attachment:absolute group-data-[orientation=vertical]/attachment:top-2 group-data-[orientation=vertical]/attachment:right-2 group-data-[orientation=vertical]/attachment:gap-0\",\n        className\n      )}\n      {...props}\n    />\n  )\n}\n\nfunction AttachmentAction({\n  className,\n  variant,\n  size = \"icon-xs\",\n  ...props\n}: React.ComponentProps<typeof Button>) {\n  return (\n    <Button\n      data-slot=\"attachment-action\"\n      variant={variant ?? \"ghost\"}\n      size={size}\n      className={cn(\n        \"rounded-full text-muted-foreground group-data-[cover=true]/attachment:text-white hover:text-foreground group-data-[cover=true]/attachment:hover:bg-white/15 group-data-[cover=true]/attachment:hover:text-white\",\n        className\n      )}\n      {...props}\n    />\n  )\n}\n\nfunction AttachmentTrigger({\n  className,\n  render,\n  type,\n  ...props\n}: useRender.ComponentProps<\"button\">) {\n  return useRender({\n    defaultTagName: \"button\",\n    props: mergeProps<\"button\">(\n      {\n        type: render ? type : (type ?? \"button\"),\n        className: cn(\"absolute inset-0 z-10 outline-none\", className),\n      },\n      props\n    ),\n    render,\n    state: {\n      slot: \"attachment-trigger\",\n    },\n  })\n}\n\nfunction AttachmentGroup({ className, ...props }: React.ComponentProps<\"div\">) {\n  return (\n    <div\n      data-slot=\"attachment-group\"\n      className={cn(\n        \"flex min-w-0 scroll-fade-x snap-x snap-mandatory scroll-px-1 scrollbar-none gap-2 overflow-x-auto overscroll-x-contain p-1 *:data-[slot=attachment]:flex-none *:data-[slot=attachment]:snap-start\",\n        className\n      )}\n      {...props}\n    />\n  )\n}\n\nexport {\n  Attachment,\n  AttachmentGroup,\n  AttachmentMedia,\n  AttachmentContent,\n  AttachmentTitle,\n  AttachmentDescription,\n  AttachmentActions,\n  AttachmentAction,\n  AttachmentTrigger,\n}\n","type":"registry:ui"}],"cssVars":{"theme":{"color-destructive-foreground":"var(--destructive-foreground)","color-fill-1":"var(--fill-1)","color-control-hover":"var(--control-hover)","text-2xs--letter-spacing":"-0.005em","text-sm--letter-spacing":"-0.005em","text-2xs":"0.75rem","text-2xs--line-height":"calc(1 / 0.75)"},"light":{"destructive-foreground":"oklch(0.505 0.213 27.518)","fill-1":"oklch(0.556 0 0 / 5%)","control-hover":"oklch(0.9925 0 0)"},"dark":{"destructive-foreground":"oklch(0.704 0.191 22.216)","fill-1":"oklch(1 0 0 / 5%)","control-hover":"oklch(0.269 0 0)"}},"categories":["chat"],"type":"registry:ui"}