{
  "$schema": "https://ui.shadcn.com/schema/registry-item.json",
  "name": "inbox-button",
  "title": "Inbox button",
  "description": "A shortcut button to an inbox whose tray glyph dips and redraws on hover or focus, with a slim four-colour provider rail along its bottom edge and an optional unread count.",
  "dependencies": [
    "motion"
  ],
  "registryDependencies": [],
  "files": [
    {
      "path": "registry/items/inbox-button/inbox-button.tsx",
      "content": "\"use client\";\n\nimport * as React from \"react\";\nimport { motion, useReducedMotionConfig, type Variants } from \"motion/react\";\n\nimport { cn } from \"@/lib/utils\";\n\n/**\n * The provider's four colours, for the rail along the bottom edge and the\n * glyph. Each default reads a variable the consumer may set (Google's own\n * colours, say) and falls back to a contract token, so the button renders in\n * any theme with nothing set.\n */\nexport const DEFAULT_PROVIDER_COLORS = [\n  \"var(--provider-blue, var(--lead))\",\n  \"var(--provider-red, var(--stop))\",\n  \"var(--provider-yellow, var(--hold))\",\n  \"var(--provider-green, var(--go))\",\n] as const;\n\nexport type ProviderColors = readonly [string, string, string, string];\n\n/** The tray dips and settles. */\nconst tray: Variants = {\n  normal: { y: 0 },\n  animate: {\n    y: [0, 2, -1, 0],\n    transition: { duration: 0.6, times: [0, 0.4, 0.7, 1], ease: \"easeInOut\" },\n  },\n};\n\n/** The tray's lip redraws itself, as if a letter just landed. */\nconst lip: Variants = {\n  normal: { pathLength: 1, opacity: 1 },\n  animate: {\n    pathLength: [0, 1],\n    opacity: [0, 1],\n    transition: { duration: 0.45, delay: 0.15, ease: [0.16, 1, 0.3, 1] },\n  },\n};\n\n/** Lucide's inbox glyph, with the tray-drop animation. */\nfunction InboxGlyph({ active }: { active: boolean }) {\n  return (\n    <motion.svg\n      aria-hidden=\"true\"\n      viewBox=\"0 0 24 24\"\n      width={20}\n      height={20}\n      fill=\"none\"\n      stroke=\"currentColor\"\n      strokeWidth={2}\n      strokeLinecap=\"round\"\n      strokeLinejoin=\"round\"\n      className=\"size-5 shrink-0\"\n      variants={tray}\n      initial=\"normal\"\n      animate={active ? \"animate\" : \"normal\"}\n    >\n      <path d=\"M5.45 5.11 2 12v6a2 2 0 0 0 2 2h16a2 2 0 0 0 2-2v-6l-3.45-6.89A2 2 0 0 0 16.76 4H7.24a2 2 0 0 0-1.79 1.11z\" />\n      <motion.polyline points=\"22 12 16 12 14 15 10 15 8 12 2 12\" variants={lip} />\n    </motion.svg>\n  );\n}\n\nexport interface InboxButtonProps {\n  /** Where the inbox lives. Renders an `<a>`; omit it and pass `onClick` for a `<button>`. */\n  href?: string;\n  onClick?: React.MouseEventHandler<HTMLElement>;\n  /** Defaults to \"View inbox\". */\n  label?: React.ReactNode;\n  /** Unread messages. Shown as a small count after the label when above zero. */\n  unreadCount?: number;\n  /** Counts above this read as \"99+\". Defaults to 99. */\n  maxCount?: number;\n  /**\n   * The provider's four colours: rail segments in order, the first also the\n   * glyph at rest and the second the glyph on hover. Any CSS colour.\n   */\n  providerColors?: ProviderColors;\n  className?: string;\n}\n\n/**\n * A shortcut to a shared inbox. The whole button drives the glyph's animation,\n * so the response belongs to the target rather than the few pixels of the\n * icon. The mail provider is present as a slim four-colour rail rather than\n * its logo: this opens your inbox experience, not the provider's screen.\n * Reduced-motion users keep the colour response without the animated glyph.\n */\nexport function InboxButton({\n  href,\n  onClick,\n  label = \"View inbox\",\n  unreadCount = 0,\n  maxCount = 99,\n  providerColors = DEFAULT_PROVIDER_COLORS,\n  className,\n}: InboxButtonProps) {\n  const reduceMotion = useReducedMotionConfig();\n  const [active, setActive] = React.useState(false);\n\n  const start = () => setActive(true);\n  const stop = () => setActive(false);\n\n  const [blue, red, yellow, green] = providerColors;\n  const unread = unreadCount > 0;\n  const countText = unreadCount > maxCount ? `${maxCount}+` : String(unreadCount);\n\n  const shared = {\n    onClick,\n    onMouseEnter: start,\n    onMouseLeave: stop,\n    onFocus: start,\n    onBlur: stop,\n    style: { \"--inbox-rest\": blue, \"--inbox-hover\": red } as React.CSSProperties,\n    className: cn(\n      \"group relative isolate inline-flex h-10 shrink-0 cursor-pointer items-center justify-center gap-2.5 overflow-hidden rounded-inset border border-rule bg-card px-5 font-body text-sm font-semibold tracking-[-0.02em] text-ink transition-colors hover:bg-paper focus-visible:outline-2 focus-visible:outline-offset-2 focus-visible:outline-lead\",\n      className,\n    ),\n  };\n\n  const content = (\n    <>\n      <span className=\"text-(--inbox-rest) transition-colors duration-200 group-hover:text-(--inbox-hover) group-focus-visible:text-(--inbox-hover)\">\n        <InboxGlyph active={active && !reduceMotion} />\n      </span>\n      {label}\n      {unread && (\n        <span className=\"-mr-1 inline-flex h-5 min-w-5 items-center justify-center rounded-full bg-lead px-1.5 text-[11px] leading-none font-semibold tabular-nums text-on-solid\">\n          {countText}\n          <span className=\"sr-only\"> unread</span>\n        </span>\n      )}\n\n      {/* A four-colour hairline, not a layout. */}\n      <span\n        aria-hidden=\"true\"\n        className=\"absolute inset-x-0 bottom-0 grid h-0.5 grid-cols-4 opacity-70 transition-opacity duration-200 group-hover:opacity-100 group-focus-visible:opacity-100\"\n      >\n        <span style={{ backgroundColor: blue }} />\n        <span style={{ backgroundColor: red }} />\n        <span style={{ backgroundColor: yellow }} />\n        <span style={{ backgroundColor: green }} />\n      </span>\n    </>\n  );\n\n  if (href !== undefined) {\n    return (\n      <a href={href} {...shared}>\n        {content}\n      </a>\n    );\n  }\n\n  return (\n    <button type=\"button\" {...shared}>\n      {content}\n    </button>\n  );\n}\n\nexport default InboxButton;\n",
      "type": "registry:ui"
    }
  ],
  "meta": {
    "kind": "primitive",
    "whenToUse": "Use as the one way into a mail inbox from a dashboard or header, where naming the mail provider by colour helps. Do not use for general navigation; a plain link or nav item is quieter.",
    "origin": {
      "project": "180Connect",
      "path": "src/components/dashboard/dashboard-inbox-button.tsx"
    },
    "credits": [
      {
        "name": "AnimateIcons (Avijit Dey), lucide/inbox-icon animation",
        "url": "https://github.com/Avijit07x/animateicons",
        "license": "MIT"
      },
      {
        "name": "Lucide, inbox icon paths",
        "url": "https://lucide.dev",
        "license": "ISC"
      }
    ]
  },
  "type": "registry:ui"
}
