{
  "$schema": "https://ui.shadcn.com/schema/registry-item.json",
  "name": "dropdown-menu-styled",
  "title": "Menu-styled dropdown",
  "description": "Styled dropdown: the Menu-styled set. 5 decorative dropdown variations (SimpleMenu, IconMenu, SectionMenu, CheckMenu, RichMenu) on the ai2 token system, powered by framer-motion, reduced-motion aware and sized sm to xl. Part of the free styled layer.",
  "dependencies": [
    "motion@^12.42.2",
    "lucide-react@^1.23.0"
  ],
  "registryDependencies": [
    "@ai2/tokens"
  ],
  "files": [
    {
      "path": "registry/ai2/styled/dropdown-menu-styled.tsx",
      "content": "\"use client\"\n\nimport * as React from \"react\"\nimport {\n  Bell,\n  Check,\n  ChevronDown,\n  CreditCard,\n  LogOut,\n  Settings,\n  Star,\n  User,\n} from \"lucide-react\"\nimport { AnimatePresence, motion, useReducedMotion } from \"motion/react\"\n\nimport { cn } from \"@/lib/utils\"\n\n/* Dropdown family: 5 decorative self-contained menus. Each export is a complete\n   dropdown: a relative inline-flex wrapper + a real trigger button + a menu panel\n   absolutely positioned BELOW the trigger (top-full mt-1). NO radix or portal. The\n   trigger toggles on CLICK; it closes on an outside click (window pointerdown, with\n   inner clicks ignored via the wrapper ref), on Escape and when an item is\n   selected. AnimatePresence fade+scale (from above); only a fade under reduced\n   motion. Color comes ONLY from tokens, via alpha color-mix. */\n\nexport type StyledSize = \"sm\" | \"md\" | \"lg\" | \"xl\"\n\nconst panelWidth: Record<StyledSize, string> = {\n  sm: \"w-48\",\n  md: \"w-56\",\n  lg: \"w-64\",\n  xl: \"w-72\",\n}\n\nconst itemHeight: Record<StyledSize, string> = {\n  sm: \"min-h-8 py-1.5\",\n  md: \"min-h-9 py-2\",\n  lg: \"min-h-10 py-2.5\",\n  xl: \"min-h-11 py-3\",\n}\n\nexport interface StyledMenuItem {\n  label: React.ReactNode\n  icon?: React.ReactNode\n  description?: React.ReactNode\n  onSelect?: () => void\n}\n\ninterface StyledMenuProps {\n  className?: string\n  size?: StyledSize\n  label?: React.ReactNode\n  items?: StyledMenuItem[]\n}\n\nconst triggerBtn =\n  \"inline-flex h-9 shrink-0 select-none items-center justify-center gap-2 whitespace-nowrap rounded-lg border border-border bg-secondary px-4 text-sm font-medium text-secondary-foreground outline-none transition-colors hover:bg-[color-mix(in_oklab,var(--color-foreground)_6%,transparent)] focus-visible:ring-[3px] focus-visible:ring-ring/50 [&_svg]:size-4 [&_svg]:shrink-0 [&_i]:text-base [&_i]:leading-none\"\n\nconst panelBase =\n  \"absolute left-0 top-full z-50 mt-1 origin-top rounded-xl border border-border bg-popover p-1.5 text-sm text-popover-foreground shadow-lg outline-none\"\n\nconst itemBase =\n  \"flex w-full cursor-default select-none items-center gap-2.5 rounded-md px-3 text-left text-sm text-popover-foreground outline-none transition-colors hover:bg-accent hover:text-accent-foreground focus-visible:bg-accent focus-visible:text-accent-foreground focus-visible:ring-[3px] focus-visible:ring-ring/50 [&_svg]:size-4 [&_svg]:shrink-0 [&_i]:text-base [&_i]:leading-none\"\n\nconst menuMotion = {\n  initial: { opacity: 0, scale: 0.96, y: -6 },\n  animate: { opacity: 1, scale: 1, y: 0 },\n  exit: { opacity: 0, scale: 0.96, y: -6 },\n  transition: { type: \"spring\" as const, stiffness: 340, damping: 26 },\n}\n\n/* Internal open/closed state management: it closes on an outside click + Escape. */\nfunction useMenuState() {\n  const [open, setOpen] = React.useState(false)\n  const wrapperRef = React.useRef<HTMLDivElement>(null)\n\n  React.useEffect(() => {\n    if (!open) return\n    const onKey = (e: KeyboardEvent) => {\n      if (e.key === \"Escape\") setOpen(false)\n    }\n    const onPointer = (e: PointerEvent) => {\n      const node = wrapperRef.current\n      if (node && e.target instanceof Node && !node.contains(e.target)) {\n        setOpen(false)\n      }\n    }\n    window.addEventListener(\"keydown\", onKey)\n    window.addEventListener(\"pointerdown\", onPointer)\n    return () => {\n      window.removeEventListener(\"keydown\", onKey)\n      window.removeEventListener(\"pointerdown\", onPointer)\n    }\n  }, [open])\n\n  return { open, setOpen, wrapperRef }\n}\n\n/* Shared shell: a relative wrapper plus a trigger button plus an AnimatePresence menu panel. */\nfunction MenuShell({\n  size = \"md\",\n  label = \"Options\",\n  className,\n  children,\n}: {\n  size?: StyledSize\n  label?: React.ReactNode\n  className?: string\n  children: (close: () => void) => React.ReactNode\n}) {\n  const { open, setOpen, wrapperRef } = useMenuState()\n  const reduce = useReducedMotion()\n\n  const fade = { initial: { opacity: 0 }, animate: { opacity: 1 }, exit: { opacity: 0 } }\n  const active = reduce ? fade : menuMotion\n\n  return (\n    <div\n      ref={wrapperRef}\n      data-slot=\"styled-dropdown-menu\"\n      className={cn(\"relative inline-flex\", className)}\n    >\n      <button\n        type=\"button\"\n        data-slot=\"styled-dropdown-menu-trigger\"\n        aria-haspopup=\"menu\"\n        aria-expanded={open}\n        className={triggerBtn}\n        onClick={() => setOpen(!open)}\n      >\n        {label}\n        <ChevronDown\n          className={cn(\"transition-transform duration-200\", open && \"rotate-180\")}\n        />\n      </button>\n\n      <AnimatePresence>\n        {open ? (\n          <motion.div\n            role=\"menu\"\n            data-slot=\"styled-dropdown-menu-content\"\n            className={cn(panelBase, panelWidth[size])}\n            initial={active.initial}\n            animate={active.animate}\n            exit={active.exit}\n            transition={reduce ? { duration: 0.16 } : menuMotion.transition}\n          >\n            {children(() => setOpen(false))}\n          </motion.div>\n        ) : null}\n      </AnimatePresence>\n    </div>\n  )\n}\n\nconst simpleItems: StyledMenuItem[] = [\n  { label: \"Profile\" },\n  { label: \"Billing\" },\n  { label: \"Settings\" },\n  { label: \"Sign out\" },\n]\n\n/* Simple: sade dikey liste. */\nexport function SimpleMenu({ className, size = \"md\", label = \"Options\", items }: StyledMenuProps) {\n  const list = items ?? simpleItems\n  return (\n    <MenuShell size={size} label={label} className={className}>\n      {(close) => (\n        <div className=\"flex flex-col\">\n          {list.map((item, i) => (\n            <button\n              key={`${i}`}\n              type=\"button\"\n              role=\"menuitem\"\n              tabIndex={0}\n              className={cn(itemBase, itemHeight[size])}\n              onClick={() => {\n                item.onSelect?.()\n                close()\n              }}\n            >\n              {item.label}\n            </button>\n          ))}\n        </div>\n      )}\n    </MenuShell>\n  )\n}\n\nconst iconItems: StyledMenuItem[] = [\n  { label: \"Account\", icon: <User /> },\n  { label: \"Billing\", icon: <CreditCard /> },\n  { label: \"Settings\", icon: <Settings /> },\n  { label: \"Sign out\", icon: <LogOut /> },\n]\n\n/* Icon: every item carries a leading token icon. */\nexport function IconMenu({ className, size = \"md\", label = \"Menu\", items }: StyledMenuProps) {\n  const list = items ?? iconItems\n  return (\n    <MenuShell size={size} label={label} className={className}>\n      {(close) => (\n        <div className=\"flex flex-col\">\n          {list.map((item, i) => (\n            <button\n              key={`${i}`}\n              type=\"button\"\n              role=\"menuitem\"\n              tabIndex={0}\n              className={cn(itemBase, itemHeight[size])}\n              onClick={() => {\n                item.onSelect?.()\n                close()\n              }}\n            >\n              {item.icon ? (\n                <span className=\"flex shrink-0 items-center text-muted-foreground\">\n                  {item.icon}\n                </span>\n              ) : null}\n              {item.label}\n            </button>\n          ))}\n        </div>\n      )}\n    </MenuShell>\n  )\n}\n\ninterface SectionMenuProps extends StyledMenuProps {\n  sections?: { heading: React.ReactNode; items: StyledMenuItem[] }[]\n}\n\nconst sectionData: { heading: React.ReactNode; items: StyledMenuItem[] }[] = [\n  {\n    heading: \"Account\",\n    items: [\n      { label: \"Profile\", icon: <User /> },\n      { label: \"Billing\", icon: <CreditCard /> },\n    ],\n  },\n  {\n    heading: \"Preferences\",\n    items: [\n      { label: \"Settings\", icon: <Settings /> },\n      { label: \"Notifications\", icon: <Bell /> },\n    ],\n  },\n]\n\n/* Section: gruplanmis itemlar, token label header + ayirici. */\nexport function SectionMenu({\n  className,\n  size = \"md\",\n  label = \"Workspace\",\n  sections,\n}: SectionMenuProps) {\n  const groups = sections ?? sectionData\n  return (\n    <MenuShell size={size} label={label} className={className}>\n      {(close) => (\n        <div className=\"flex flex-col\">\n          {groups.map((group, gi) => (\n            <div key={`${gi}`} className=\"flex flex-col\">\n              {gi > 0 ? (\n                <div\n                  role=\"separator\"\n                  className=\"my-1.5 h-px bg-[color-mix(in_oklab,var(--color-border)_100%,transparent)]\"\n                />\n              ) : null}\n              <div className=\"px-3 py-1.5 text-xs font-medium text-muted-foreground\">\n                {group.heading}\n              </div>\n              {group.items.map((item, i) => (\n                <button\n                  key={`${gi}-${i}`}\n                  type=\"button\"\n                  role=\"menuitem\"\n                  tabIndex={0}\n                  className={cn(itemBase, itemHeight[size])}\n                  onClick={() => {\n                    item.onSelect?.()\n                    close()\n                  }}\n                >\n                  {item.icon ? (\n                    <span className=\"flex shrink-0 items-center text-muted-foreground\">\n                      {item.icon}\n                    </span>\n                  ) : null}\n                  {item.label}\n                </button>\n              ))}\n            </div>\n          ))}\n        </div>\n      )}\n    </MenuShell>\n  )\n}\n\nconst checkItems: StyledMenuItem[] = [\n  { label: \"Show sidebar\" },\n  { label: \"Show toolbar\" },\n  { label: \"Show status bar\" },\n  { label: \"Compact mode\" },\n]\n\n/* Check: checkable items toggle the token check mark; the menu stays open. */\nexport function CheckMenu({ className, size = \"md\", label = \"View\", items }: StyledMenuProps) {\n  const list = items ?? checkItems\n  const [checked, setChecked] = React.useState<Record<number, boolean>>({ 0: true })\n\n  const toggle = (i: number, item: StyledMenuItem) => {\n    setChecked((prev) => ({ ...prev, [i]: !prev[i] }))\n    item.onSelect?.()\n  }\n\n  return (\n    <MenuShell size={size} label={label} className={className}>\n      {() => (\n        <div className=\"flex flex-col\">\n          {list.map((item, i) => {\n            const isChecked = !!checked[i]\n            return (\n              <button\n                key={`${i}`}\n                type=\"button\"\n                role=\"menuitemcheckbox\"\n                aria-checked={isChecked}\n                tabIndex={0}\n                className={cn(itemBase, itemHeight[size])}\n                onClick={() => toggle(i, item)}\n              >\n                <span className=\"flex size-4 shrink-0 items-center justify-center text-primary\">\n                  {isChecked ? <Check /> : null}\n                </span>\n                {item.label}\n              </button>\n            )\n          })}\n        </div>\n      )}\n    </MenuShell>\n  )\n}\n\nconst richItems: StyledMenuItem[] = [\n  {\n    label: \"Free plan\",\n    icon: <User />,\n    description: \"Up to 3 projects and basic support.\",\n  },\n  {\n    label: \"Pro plan\",\n    icon: <Star />,\n    description: \"Unlimited projects and priority support.\",\n  },\n  {\n    label: \"Billing\",\n    icon: <CreditCard />,\n    description: \"Manage invoices and payment methods.\",\n  },\n]\n\n/* Rich: every item carries a title plus a small description line. */\nexport function RichMenu({ className, size = \"lg\", label = \"Plan\", items }: StyledMenuProps) {\n  const list = items ?? richItems\n  return (\n    <MenuShell size={size} label={label} className={className}>\n      {(close) => (\n        <div className=\"flex flex-col\">\n          {list.map((item, i) => (\n            <button\n              key={`${i}`}\n              type=\"button\"\n              role=\"menuitem\"\n              tabIndex={0}\n              className={cn(\n                \"flex w-full cursor-default select-none items-start gap-3 rounded-md px-3 py-2.5 text-left outline-none transition-colors hover:bg-accent hover:text-accent-foreground focus-visible:bg-accent focus-visible:text-accent-foreground focus-visible:ring-[3px] focus-visible:ring-ring/50 [&_svg]:size-4 [&_svg]:shrink-0 [&_i]:text-base [&_i]:leading-none\"\n              )}\n              onClick={() => {\n                item.onSelect?.()\n                close()\n              }}\n            >\n              {item.icon ? (\n                <span className=\"mt-0.5 flex shrink-0 items-center text-muted-foreground\">\n                  {item.icon}\n                </span>\n              ) : null}\n              <span className=\"flex flex-col gap-0.5\">\n                <span className=\"text-sm font-medium text-popover-foreground\">{item.label}</span>\n                {item.description ? (\n                  <span className=\"text-xs text-muted-foreground\">{item.description}</span>\n                ) : null}\n              </span>\n            </button>\n          ))}\n        </div>\n      )}\n    </MenuShell>\n  )\n}\n",
      "type": "registry:component",
      "target": "components/ui/dropdown-menu-styled.tsx"
    }
  ],
  "categories": [
    "styled",
    "dropdown"
  ],
  "type": "registry:component"
}