{
  "$schema": "https://ui.shadcn.com/schema/registry-item.json",
  "name": "notification-inbox",
  "type": "registry:ui",
  "title": "Notification inbox",
  "description": "A bell with an unread count that opens a notification popover: All and Mentions tabs, unread dots, mark all read, type icons and an empty state. Built in React.",
  "dependencies": [
    "@base-ui/react",
    "lucide-react"
  ],
  "registryDependencies": [
    "utils"
  ],
  "files": [
    {
      "path": "registry/ui/notification-inbox.tsx",
      "type": "registry:ui",
      "target": "components/ui/notification-inbox.tsx",
      "content": "\"use client\"\nimport * as React from \"react\"\nimport { Popover as PopoverPrimitive } from \"@base-ui/react/popover\"\nimport { AtSignIcon, BellIcon, CheckCheckIcon, GitPullRequestIcon, MessageSquareIcon, UserPlusIcon } from \"lucide-react\"\nimport { cn } from \"@/lib/utils\"\n\ntype Note = {\n  id: string\n  kind: \"mention\" | \"comment\" | \"invite\" | \"review\"\n  who: string\n  text: string\n  time: string\n  unread?: boolean\n}\n\nconst SAMPLE: Note[] = [\n  { id: \"1\", kind: \"mention\", who: \"Mira Sato\", text: \"mentioned you in Q3 roadmap: “@ada can you own pricing?”\", time: \"2m\", unread: true },\n  { id: \"2\", kind: \"review\", who: \"Leo Brandt\", text: \"requested your review on #482 Billing redesign\", time: \"18m\", unread: true },\n  { id: \"3\", kind: \"comment\", who: \"Tomás Reyes\", text: \"replied to your comment on Invoices\", time: \"1h\", unread: true },\n  { id: \"4\", kind: \"invite\", who: \"Northwind Labs\", text: \"invited you to the Finance workspace\", time: \"Yesterday\" },\n  { id: \"5\", kind: \"comment\", who: \"Ada Okafor\", text: \"resolved the thread on Onboarding copy\", time: \"Mon\" },\n]\n\nconst KIND = {\n  mention: { icon: AtSignIcon, tone: \"text-accent-fg\" },\n  comment: { icon: MessageSquareIcon, tone: \"text-fg-muted\" },\n  invite: { icon: UserPlusIcon, tone: \"text-success\" },\n  review: { icon: GitPullRequestIcon, tone: \"text-info\" },\n}\n\n/**\n * NotificationInbox — a bell with an unread count that opens an inbox:\n * tabs for All and Mentions, unread dots, mark-all-read, and an empty state.\n */\nfunction NotificationInbox({ notes: initial = SAMPLE, className }: { notes?: Note[]; className?: string }) {\n  const [notes, setNotes] = React.useState(initial)\n  const [tab, setTab] = React.useState<\"all\" | \"mentions\">(\"all\")\n  const unread = notes.filter((n) => n.unread).length\n  const list = tab === \"all\" ? notes : notes.filter((n) => n.kind === \"mention\")\n  return (\n    <PopoverPrimitive.Root>\n      <PopoverPrimitive.Trigger\n        className={cn(\n          \"relative inline-flex size-9 items-center justify-center rounded-lg border border-border bg-surface text-fg-muted shadow-key outline-none transition-colors hover:text-fg focus-visible:ring-2 focus-visible:ring-accent data-popup-open:text-fg\",\n          className\n        )}\n        aria-label={unread ? `Notifications, ${unread} unread` : \"Notifications\"}\n      >\n        <BellIcon className=\"size-4\" />\n        {unread ? (\n          <span className=\"absolute -top-1 -right-1 grid h-4 min-w-4 place-items-center rounded-full border-2 border-bg bg-accent px-1 text-[9px] font-semibold text-on-accent tabular-nums\">{unread}</span>\n        ) : null}\n      </PopoverPrimitive.Trigger>\n      <PopoverPrimitive.Portal>\n        <PopoverPrimitive.Positioner sideOffset={8} align=\"end\" className=\"z-50\">\n          <PopoverPrimitive.Popup className=\"w-[380px] max-w-[calc(100vw-2rem)] origin-(--transform-origin) overflow-hidden rounded-2xl border border-border bg-raised text-fg shadow-overlay outline-none transition-[opacity,transform] duration-200 ease-hairline data-[ending-style]:scale-[0.98] data-[ending-style]:opacity-0 data-[starting-style]:scale-[0.98] data-[starting-style]:opacity-0\">\n            <div className=\"flex items-center justify-between border-b border-border px-4 pt-3\">\n              <div className=\"flex gap-4\">\n                {([\"all\", \"mentions\"] as const).map((t) => (\n                  <button\n                    key={t}\n                    type=\"button\"\n                    onClick={() => setTab(t)}\n                    aria-pressed={tab === t}\n                    className={cn(\"relative pb-2.5 text-[0.8125rem] font-medium capitalize outline-none\", tab === t ? \"text-fg\" : \"text-fg-muted hover:text-fg\")}\n                  >\n                    {t}\n                    {t === \"all\" && unread ? <span className=\"ml-1.5 rounded-full bg-accent-soft px-1.5 text-[10px] text-accent-fg\">{unread}</span> : null}\n                    {tab === t ? <span className=\"absolute inset-x-0 -bottom-px h-[2px] rounded-full bg-fg\" /> : null}\n                  </button>\n                ))}\n              </div>\n              <button\n                type=\"button\"\n                disabled={!unread}\n                onClick={() => setNotes((n) => n.map((x) => ({ ...x, unread: false })))}\n                className=\"-mt-2 inline-flex items-center gap-1 rounded-md px-1.5 py-1 text-xs text-fg-muted outline-none hover:bg-sunken hover:text-fg disabled:opacity-40\"\n              >\n                <CheckCheckIcon className=\"size-3.5\" /> Mark all read\n              </button>\n            </div>\n            <ul className=\"max-h-[360px] overflow-y-auto p-1.5\">\n              {list.length ? (\n                list.map((n) => {\n                  const K = KIND[n.kind]\n                  return (\n                    <li key={n.id}>\n                      <button\n                        type=\"button\"\n                        onClick={() => setNotes((all) => all.map((x) => (x.id === n.id ? { ...x, unread: false } : x)))}\n                        className=\"flex w-full items-start gap-3 rounded-xl p-2.5 text-left outline-none transition-colors hover:bg-sunken focus-visible:bg-sunken\"\n                      >\n                        <span className=\"relative\">\n                          <span className=\"grid size-8 place-items-center rounded-full border border-border bg-sunken text-[10px] font-semibold text-fg-muted\">\n                            {n.who.split(\" \").map((p) => p[0]).join(\"\").slice(0, 2)}\n                          </span>\n                          <span className={cn(\"absolute -right-1 -bottom-1 grid size-4 place-items-center rounded-full border border-border bg-raised\", K.tone)}>\n                            <K.icon className=\"size-2.5\" />\n                          </span>\n                        </span>\n                        <span className=\"min-w-0 flex-1\">\n                          <span className=\"block text-[0.8125rem] leading-[1.45] text-fg-muted\">\n                            <span className=\"font-medium text-fg\">{n.who}</span> {n.text}\n                          </span>\n                          <span className=\"mt-0.5 block text-[11px] text-fg-subtle\">{n.time}</span>\n                        </span>\n                        {n.unread ? <span className=\"mt-1.5 size-2 shrink-0 rounded-full bg-accent\" aria-label=\"Unread\" /> : null}\n                      </button>\n                    </li>\n                  )\n                })\n              ) : (\n                <li className=\"grid place-items-center gap-1 px-6 py-12 text-center\">\n                  <AtSignIcon className=\"size-5 text-fg-subtle\" />\n                  <p className=\"mt-1 text-[0.8125rem] font-medium text-fg\">No mentions yet</p>\n                  <p className=\"text-xs text-fg-muted\">When someone @mentions you, it shows up here.</p>\n                </li>\n              )}\n            </ul>\n            <div className=\"border-t border-border bg-sunken/50 px-4 py-2.5 text-center\">\n              <a href=\"#\" className=\"text-xs font-medium text-fg-muted hover:text-fg\">View all activity</a>\n            </div>\n          </PopoverPrimitive.Popup>\n        </PopoverPrimitive.Positioner>\n      </PopoverPrimitive.Portal>\n    </PopoverPrimitive.Root>\n  )\n}\nexport { NotificationInbox }\n"
    }
  ]
}
