{
  "$schema": "https://uipkge.dev/schema/registry-item.json",
  "name": "attachment",
  "title": "Attachment",
  "type": "registry:ui",
  "description": "Single file or image chip. Drive look with props: state, size, orientation, media, src, removable. Not a dropzone (see file-upload).",
  "categories": [
    "data-display"
  ],
  "files": [
    {
      "path": "packages/elements-lit/src/components/attachment/attachment.ts",
      "content": "import { LitElement, css, html, nothing } from 'lit'\nimport { FileCode, FileText, Image, Loader2, X } from 'lucide'\nimport { cn } from '../../lib/utils'\nimport { tailwind } from '../../lib/styles'\nimport { ThemeController } from '../../lib/theme'\nimport { icon } from '../../lib/icon'\nimport { attachmentMediaVariants, attachmentVariants, type AttachmentVariants } from './attachment.variants'\n\ntype Size = NonNullable<AttachmentVariants['size']>\ntype Orientation = NonNullable<AttachmentVariants['orientation']>\ntype State = 'idle' | 'uploading' | 'processing' | 'error' | 'done'\ntype Media = 'file' | 'image' | 'code'\n\n/**\n * <uip-attachment> — file and media attachment chip.\n */\nexport class UipAttachment extends LitElement {\n  static styles = [tailwind, css`:host { display: inline-flex; }`]\n\n  static properties = {\n    title: { reflect: true },\n    description: { reflect: true },\n    state: { reflect: true },\n    size: { reflect: true },\n    orientation: { reflect: true },\n    media: { reflect: true },\n    src: { reflect: true },\n    alt: { reflect: true },\n    removable: { type: Boolean, reflect: true },\n  }\n\n  override title = ''\n  description?: string\n  state: State = 'done'\n  size: Size = 'default'\n  orientation: Orientation = 'horizontal'\n  media: Media = 'file'\n  src?: string\n  alt = ''\n  removable = false\n\n  constructor() {\n    super()\n    new ThemeController(this)\n  }\n\n  connectedCallback() {\n    super.connectedCallback()\n    this.setAttribute('data-uipkge', '')\n    this.setAttribute('data-slot', 'attachment')\n  }\n\n  willUpdate() {\n    this.setAttribute('data-state', this.state)\n    this.setAttribute('data-size', this.size)\n    this.setAttribute('data-orientation', this.orientation)\n  }\n\n  private onRemoveClick(e: MouseEvent) {\n    e.stopPropagation()\n    this.dispatchEvent(new CustomEvent('remove', { bubbles: true, composed: true }))\n  }\n\n  render() {\n    const busy = this.state === 'uploading' || this.state === 'processing'\n    const size = this.size\n    const orientation = this.orientation\n    const state = this.state\n    const media = this.media\n\n    let mediaContent\n    if (this.src && media === 'image' && !busy) {\n      mediaContent = html`<img src=${this.src} alt=${this.alt} class=\"size-full object-cover\" />`\n    } else if (busy) {\n      mediaContent = icon(Loader2, 'loader-2', 'size-4 motion-safe:animate-spin')\n    } else if (media === 'code') {\n      mediaContent = icon(FileCode, 'file-code')\n    } else if (media === 'image') {\n      mediaContent = icon(Image, 'image')\n    } else {\n      mediaContent = icon(FileText, 'file-text')\n    }\n\n    return html`\n      <div\n        part=\"base\"\n        data-slot=\"attachment\"\n        data-state=${state}\n        data-size=${size}\n        data-orientation=${orientation}\n        class=${cn(attachmentVariants({ size, orientation }))}\n      >\n        <div data-slot=\"attachment-media\" class=${cn(attachmentMediaVariants({ size }))}>\n          ${mediaContent}\n        </div>\n        <div data-slot=\"attachment-content\" class=\"min-w-0 flex-1 leading-tight\">\n          <span data-slot=\"attachment-title\" class=${cn('block truncate font-medium', busy && 'animate-pulse')}>\n            ${this.title}\n          </span>\n          ${this.description\n            ? html`\n                <span\n                  data-slot=\"attachment-description\"\n                  class=${cn(\n                    'text-muted-foreground mt-0.5 block truncate text-xs',\n                    state === 'error' && 'text-destructive/80',\n                  )}\n                >\n                  ${this.description}\n                </span>\n              `\n            : nothing}\n        </div>\n        ${this.removable\n          ? html`\n              <button\n                type=\"button\"\n                data-slot=\"attachment-remove\"\n                class=\"text-muted-foreground hover:bg-accent hover:text-foreground relative z-10 inline-flex size-7 shrink-0 items-center justify-center rounded-md\"\n                aria-label=${`Remove ${this.title}`}\n                @click=${this.onRemoveClick}\n              >\n                ${icon(X, 'x', 'size-3.5')}\n              </button>\n            `\n          : nothing}\n      </div>\n    `\n  }\n}\n\ncustomElements.get('uip-attachment') || customElements.define('uip-attachment', UipAttachment)\n\ndeclare global {\n  interface HTMLElementTagNameMap {\n    'uip-attachment': UipAttachment\n  }\n}\n",
      "type": "registry:file",
      "target": "src/uip/components/attachment/attachment.ts"
    },
    {
      "path": "packages/elements-lit/src/components/attachment/attachment.variants.ts",
      "content": "import type { VariantProps } from 'class-variance-authority'\nimport { cva } from 'class-variance-authority'\n\nexport const attachmentVariants = cva(\n  'group/attachment relative flex w-fit max-w-full min-w-0 shrink-0 rounded-xl border bg-card text-card-foreground transition-colors data-[state=error]:border-destructive/30 data-[state=idle]:border-dashed',\n  {\n    variants: {\n      size: {\n        default: 'gap-2 p-2 text-sm',\n        sm: 'gap-2 p-1.5 text-xs',\n        xs: 'gap-1.5 rounded-lg p-1 text-xs',\n      },\n      orientation: {\n        horizontal: 'min-w-40 items-center',\n        vertical: 'w-24 flex-col',\n      },\n    },\n    defaultVariants: {\n      size: 'default',\n      orientation: 'horizontal',\n    },\n  },\n)\n\nexport const attachmentMediaVariants = cva(\n  \"relative flex aspect-square shrink-0 items-center justify-center overflow-hidden rounded-lg bg-muted text-foreground group-data-[orientation=vertical]/attachment:w-full group-data-[state=error]/attachment:bg-destructive/10 group-data-[state=error]/attachment:text-destructive [&_svg]:pointer-events-none [&_svg:not([class*='size-'])]:size-4\",\n  {\n    variants: {\n      size: {\n        default: 'w-10',\n        sm: 'w-8',\n        xs: 'w-7 rounded-md [&_svg:not([class*=\"size-\"])]:size-3.5',\n      },\n    },\n    defaultVariants: {\n      size: 'default',\n    },\n  },\n)\n\nexport type AttachmentVariants = VariantProps<typeof attachmentVariants>\n",
      "type": "registry:file",
      "target": "src/uip/components/attachment/attachment.variants.ts"
    }
  ],
  "dependencies": [
    "class-variance-authority",
    "lit",
    "lucide"
  ],
  "registryDependencies": [
    "https://uipkge.dev/r/lit/lit-core.json"
  ]
}
