{
  "$schema": "https://ui.shadcn.com/schema/registry-item.json",
  "name": "menubar-accent",
  "title": "Accent menubar",
  "description": "Styled menubar: the Accent set. 5 decorative menubar variations (BarMenubarAccent, GlowMenubar, FillMenubar, UnderlineMenubar, EdgeMenubar) 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"
  ],
  "registryDependencies": [
    "@ai2/tokens"
  ],
  "files": [
    {
      "path": "registry/ai2/styled/menubar-accent.tsx",
      "content": "\"use client\"\n\nimport * as React from \"react\"\nimport { AnimatePresence, motion, useReducedMotion } from \"motion/react\"\n\nimport { cn } from \"@/lib/utils\"\n\n/* Accent menubar family: 5 decorative, SELF-SUFFICIENT menubars. NO radix or\n   portal. The menu content and the motion are the same in every variant; the whole\n   difference is the highlight technique of the OPEN/ACTIVE top-level menu: a soft\n   accent surface, a primary glow, a filled primary, an underline and a left-edge\n   strip. Classic menubar behavior: while one menu is open, hovering a sibling\n   trigger switches to that menu. It closes on an outside click, on Escape (focus\n   returns to the trigger) and on an item selection. Left/Right arrows move between\n   the top-level menus and Up/Down moves inside the open one. No transform under\n   reduced motion. Color comes ONLY from tokens, via alpha color-mix. */\n\nexport type StyledSize = \"sm\" | \"md\" | \"lg\" | \"xl\"\n\ntype MenuItem = { label: React.ReactNode; onSelect?: () => void }\ntype Menu = { label: React.ReactNode; items: MenuItem[] }\n\ninterface MenubarProps {\n  className?: string\n  size?: StyledSize\n  menus?: Menu[]\n}\n\nconst defaultMenus: Menu[] = [\n  {\n    label: \"File\",\n    items: [\n      { label: \"New File\" },\n      { label: \"Open...\" },\n      { label: \"Save\" },\n      { label: \"Save As...\" },\n    ],\n  },\n  {\n    label: \"Edit\",\n    items: [\n      { label: \"Undo\" },\n      { label: \"Redo\" },\n      { label: \"Cut\" },\n      { label: \"Copy\" },\n      { label: \"Paste\" },\n    ],\n  },\n  {\n    label: \"View\",\n    items: [{ label: \"Zoom In\" }, { label: \"Zoom Out\" }, { label: \"Full Screen\" }],\n  },\n]\n\nconst barSize: Record<StyledSize, string> = {\n  sm: \"h-8 gap-0.5 px-1 text-xs\",\n  md: \"h-9 gap-1 px-1 text-sm\",\n  lg: \"h-10 gap-1 px-1.5 text-sm\",\n  xl: \"h-11 gap-1.5 px-2 text-base\",\n}\n\nconst triggerSize: Record<StyledSize, string> = {\n  sm: \"h-6 px-2\",\n  md: \"h-7 px-2.5\",\n  lg: \"h-8 px-3\",\n  xl: \"h-9 px-3.5\",\n}\n\nconst panelSize: Record<StyledSize, string> = {\n  sm: \"min-w-40 text-xs\",\n  md: \"min-w-44 text-sm\",\n  lg: \"min-w-48 text-sm\",\n  xl: \"min-w-52 text-base\",\n}\n\nconst itemSize: Record<StyledSize, string> = {\n  sm: \"px-2 py-1.5\",\n  md: \"px-2.5 py-1.5\",\n  lg: \"px-3 py-2\",\n  xl: \"px-3.5 py-2\",\n}\n\nconst softHover =\n  \"hover:bg-[color-mix(in_oklab,var(--color-foreground)_6%,transparent)]\"\n\nconst triggerBase =\n  \"inline-flex select-none items-center gap-1.5 whitespace-nowrap rounded-md font-medium outline-none transition-colors focus-visible:ring-[3px] focus-visible:ring-ring/50 [&>svg]:size-4 [&>svg]:shrink-0 [&>i]:text-base [&>i]:leading-none\"\n\nconst itemBase =\n  \"flex w-full cursor-default select-none items-center gap-2 rounded-md text-left text-popover-foreground outline-none transition-colors 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.5 origin-top rounded-lg border border-border bg-popover p-1 text-popover-foreground shadow-lg outline-none\"\n\n/* The shared menubar core. The panel is always absolutely positioned inside the\n   relative wrapper of its own trigger, so many examples on a single page never\n   collide. */\nfunction MenubarCore({\n  props,\n  barClassName,\n  triggerClassName,\n  panelClassName,\n}: {\n  props: MenubarProps\n  barClassName?: string\n  triggerClassName: (active: boolean) => string\n  panelClassName?: string\n}) {\n  const { className, size = \"md\", menus = defaultMenus } = props\n  const reduce = useReducedMotion()\n  const uid = React.useId()\n  const [openIndex, setOpenIndex] = React.useState<number | null>(null)\n  const [autoFocusItem, setAutoFocusItem] = React.useState(false)\n  const rootRef = React.useRef<HTMLDivElement>(null)\n  const triggerRefs = React.useRef<Array<HTMLButtonElement | null>>([])\n  const itemRefs = React.useRef<Array<HTMLButtonElement | null>>([])\n\n  React.useEffect(() => {\n    if (openIndex === null) return\n    const onKey = (e: KeyboardEvent) => {\n      if (e.key !== \"Escape\") return\n      triggerRefs.current[openIndex]?.focus()\n      setOpenIndex(null)\n    }\n    const onPointer = (e: PointerEvent) => {\n      const node = rootRef.current\n      if (node && e.target instanceof Node && !node.contains(e.target)) {\n        setOpenIndex(null)\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  }, [openIndex])\n\n  React.useEffect(() => {\n    if (openIndex === null || !autoFocusItem) return\n    const id = window.requestAnimationFrame(() => itemRefs.current[0]?.focus())\n    setAutoFocusItem(false)\n    return () => window.cancelAnimationFrame(id)\n  }, [openIndex, autoFocusItem])\n\n  const moveTrigger = (from: number, dir: 1 | -1) => {\n    const next = (from + dir + menus.length) % menus.length\n    triggerRefs.current[next]?.focus()\n    if (openIndex !== null) setOpenIndex(next)\n  }\n\n  const onTriggerKeyDown = (e: React.KeyboardEvent, i: number) => {\n    if (e.key === \"ArrowRight\") {\n      e.preventDefault()\n      moveTrigger(i, 1)\n    } else if (e.key === \"ArrowLeft\") {\n      e.preventDefault()\n      moveTrigger(i, -1)\n    } else if (e.key === \"ArrowDown\") {\n      e.preventDefault()\n      setOpenIndex(i)\n      setAutoFocusItem(true)\n    }\n  }\n\n  const onItemKeyDown = (e: React.KeyboardEvent, j: number, count: number, i: number) => {\n    if (e.key === \"ArrowDown\") {\n      e.preventDefault()\n      itemRefs.current[(j + 1) % count]?.focus()\n    } else if (e.key === \"ArrowUp\") {\n      e.preventDefault()\n      itemRefs.current[(j - 1 + count) % count]?.focus()\n    } else if (e.key === \"ArrowRight\" || e.key === \"ArrowLeft\") {\n      e.preventDefault()\n      moveTrigger(i, e.key === \"ArrowRight\" ? 1 : -1)\n      setAutoFocusItem(true)\n    }\n  }\n\n  const enter = reduce\n    ? { initial: { opacity: 0 }, animate: { opacity: 1 }, exit: { opacity: 0 } }\n    : {\n        initial: { opacity: 0, scale: 0.97, y: -4 },\n        animate: { opacity: 1, scale: 1, y: 0 },\n        exit: { opacity: 0, scale: 0.97, y: -4 },\n      }\n\n  return (\n    <div\n      ref={rootRef}\n      data-slot=\"styled-menubar\"\n      role=\"menubar\"\n      className={cn(\"inline-flex items-center\", barSize[size], barClassName, className)}\n    >\n      {menus.map((menu, i) => {\n        const isOpen = openIndex === i\n        const triggerId = `${uid}-trigger-${i}`\n        const menuId = `${uid}-menu-${i}`\n        return (\n          <div key={i} className=\"relative\">\n            <button\n              type=\"button\"\n              id={triggerId}\n              ref={(node) => {\n                triggerRefs.current[i] = node\n              }}\n              role=\"menuitem\"\n              aria-haspopup=\"menu\"\n              aria-expanded={isOpen}\n              aria-controls={isOpen ? menuId : undefined}\n              onClick={() => setOpenIndex(isOpen ? null : i)}\n              onPointerEnter={() => {\n                if (openIndex !== null) setOpenIndex(i)\n              }}\n              onKeyDown={(e) => onTriggerKeyDown(e, i)}\n              className={cn(triggerBase, triggerSize[size], triggerClassName(isOpen))}\n            >\n              {menu.label}\n            </button>\n\n            <AnimatePresence>\n              {isOpen ? (\n                <motion.div\n                  id={menuId}\n                  role=\"menu\"\n                  aria-labelledby={triggerId}\n                  className={cn(panelBase, panelSize[size], panelClassName)}\n                  initial={enter.initial}\n                  animate={enter.animate}\n                  exit={enter.exit}\n                  transition={\n                    reduce\n                      ? { duration: 0.12 }\n                      : { type: \"spring\" as const, stiffness: 360, damping: 26 }\n                  }\n                >\n                  {menu.items.map((item, j) => (\n                    <button\n                      key={j}\n                      type=\"button\"\n                      role=\"menuitem\"\n                      ref={(node) => {\n                        if (openIndex === i) itemRefs.current[j] = node\n                      }}\n                      onClick={() => {\n                        item.onSelect?.()\n                        triggerRefs.current[i]?.focus()\n                        setOpenIndex(null)\n                      }}\n                      onKeyDown={(e) => onItemKeyDown(e, j, menu.items.length, i)}\n                      className={cn(itemBase, itemSize[size], softHover)}\n                    >\n                      {item.label}\n                    </button>\n                  ))}\n                </motion.div>\n              ) : null}\n            </AnimatePresence>\n          </div>\n        )\n      })}\n    </div>\n  )\n}\n\n/* BarAccent: acik menu yumusak bir accent yuzey ve ince kenarlik alir. */\nexport function BarMenubarAccent({ className, size = \"md\", menus }: MenubarProps) {\n  return (\n    <MenubarCore\n      props={{ className, size, menus }}\n      barClassName=\"rounded-lg border border-border bg-card shadow-xs\"\n      triggerClassName={(active) =>\n        active\n          ? \"border border-border bg-accent text-accent-foreground\"\n          : cn(\"border border-transparent text-muted-foreground hover:text-foreground\", softHover)\n      }\n    />\n  )\n}\n\n/* Glow: the open menu glows with a primary halo (a ring + a soft shadow). */\nexport function GlowMenubar({ className, size = \"md\", menus }: MenubarProps) {\n  return (\n    <MenubarCore\n      props={{ className, size, menus }}\n      barClassName=\"rounded-lg border border-border bg-card shadow-xs\"\n      triggerClassName={(active) =>\n        active\n          ? \"bg-[color-mix(in_oklab,var(--color-primary)_14%,transparent)] text-foreground shadow-[0_0_0_1px_color-mix(in_oklab,var(--color-primary)_45%,transparent),0_0_14px_color-mix(in_oklab,var(--color-primary)_35%,transparent)]\"\n          : cn(\"text-muted-foreground hover:text-foreground\", softHover)\n      }\n      panelClassName=\"shadow-[0_8px_24px_color-mix(in_oklab,var(--color-primary)_18%,transparent)]\"\n    />\n  )\n}\n\n/* Fill: acik menu tamamen primary ile dolar. */\nexport function FillMenubar({ className, size = \"md\", menus }: MenubarProps) {\n  return (\n    <MenubarCore\n      props={{ className, size, menus }}\n      barClassName=\"rounded-lg border border-border bg-card shadow-xs\"\n      triggerClassName={(active) =>\n        active\n          ? \"bg-primary text-primary-foreground shadow-sm\"\n          : cn(\"text-muted-foreground hover:text-foreground\", softHover)\n      }\n    />\n  )\n}\n\n/* Underline: a thick primary line appears under the open menu. */\nexport function UnderlineMenubar({ className, size = \"md\", menus }: MenubarProps) {\n  return (\n    <MenubarCore\n      props={{ className, size, menus }}\n      barClassName=\"border-b border-border bg-transparent\"\n      triggerClassName={(active) =>\n        cn(\n          \"rounded-none border-b-2\",\n          active\n            ? \"border-primary text-foreground\"\n            : \"border-transparent text-muted-foreground hover:border-[color-mix(in_oklab,var(--color-foreground)_20%,transparent)] hover:text-foreground\"\n        )\n      }\n    />\n  )\n}\n\n/* Edge: acik menunun sol kenarinda dikey bir primary serit belirir. */\nexport function EdgeMenubar({ className, size = \"md\", menus }: MenubarProps) {\n  return (\n    <MenubarCore\n      props={{ className, size, menus }}\n      barClassName=\"rounded-lg border border-border bg-card shadow-xs\"\n      triggerClassName={(active) =>\n        cn(\n          \"rounded-l-none rounded-r-md border-l-2\",\n          active\n            ? \"border-primary bg-[color-mix(in_oklab,var(--color-primary)_10%,transparent)] text-foreground\"\n            : cn(\"border-transparent text-muted-foreground hover:text-foreground\", softHover)\n        )\n      }\n      panelClassName=\"border-l-2 border-l-primary\"\n    />\n  )\n}\n",
      "type": "registry:component",
      "target": "components/ui/menubar-accent.tsx"
    }
  ],
  "categories": [
    "styled",
    "menubar"
  ],
  "type": "registry:component"
}