{
  "$schema": "https://ui.shadcn.com/schema/registry-item.json",
  "name": "native-action-dropdown-shadcnui",
  "type": "registry:component",
  "title": "Native Action Dropdown",
  "description": "A click-driven action picker whose multi-level submenu opens beside the row, jumps straight to the selected item, and drills in place with an animated breadcrumb at a fixed width.",
  "dependencies": [
    "framer-motion",
    "react"
  ],
  "files": [
    {
      "path": "@uitripled/react-shadcn/src/components/native/native-action-dropdown-shadcnui.tsx",
      "content": "\"use client\";\n\nimport { cn } from \"@/lib/utils\";\nimport { AnimatePresence, motion, useReducedMotion } from \"framer-motion\";\nimport { Check, ChevronDown, ChevronRight } from \"lucide-react\";\nimport {\n  useCallback,\n  useEffect,\n  useId,\n  useLayoutEffect,\n  useMemo,\n  useRef,\n  useState,\n} from \"react\";\n\nexport interface ActionDropdownNode {\n  /** Unique id. */\n  id: string;\n  /** Display name. */\n  name: string;\n  /** Secondary line shown under the name. */\n  detail?: string;\n  /** Small pill badge next to the name. */\n  meta?: string;\n  /** Trigger subtitle shown when this leaf is the value. */\n  summary?: string;\n  /** Optional root-level group label (sections render in first-seen order). */\n  group?: string;\n  /** Nested children — a node with children is a submenu parent. */\n  children?: ActionDropdownNode[];\n}\n\nexport interface NativeActionDropdownProps {\n  /** The (recursive) option tree. */\n  items: ActionDropdownNode[];\n  /** Controlled selected id. */\n  value?: string;\n  /** Uncontrolled initial selected id. */\n  defaultValue?: string;\n  /** Fired with the selected leaf id. */\n  onValueChange?: (id: string) => void;\n  /** Panel header title. */\n  label?: string;\n  /** Panel header subtitle. */\n  description?: string;\n  /** Trigger text when nothing is selected. */\n  placeholder?: string;\n  className?: string;\n}\n\n// Walk the tree to the node with `id`, returning the root→node chain.\nfunction findChain(\n  nodes: ActionDropdownNode[],\n  id: string\n): ActionDropdownNode[] | null {\n  for (const node of nodes) {\n    if (node.id === id) return [node];\n    if (node.children) {\n      const sub = findChain(node.children, id);\n      if (sub) return [node, ...sub];\n    }\n  }\n  return null;\n}\n\nfunction resolveSelection(\n  items: ActionDropdownNode[],\n  id: string,\n  placeholder: string\n): { name: string; summary: string } {\n  const chain = findChain(items, id);\n  if (!chain || chain.length === 0) return { name: placeholder, summary: \"\" };\n  const leaf = chain[chain.length - 1];\n  const parent = chain.length > 1 ? chain[chain.length - 2] : null;\n  return {\n    name: leaf.name,\n    summary: parent ? parent.name : (leaf.summary ?? \"\"),\n  };\n}\n\n// The selected value expressed as a column-stack (one active index per level).\nfunction colsForSelection(\n  root: ActionDropdownNode[],\n  items: ActionDropdownNode[],\n  id: string\n): number[] {\n  const chain = findChain(items, id);\n  if (!chain) return [0];\n  const cols: number[] = [];\n  let level = root;\n  for (const node of chain) {\n    const idx = level.findIndex((item) => item.id === node.id);\n    if (idx < 0) break;\n    cols.push(idx);\n    level = node.children ?? [];\n  }\n  return cols.length ? cols : [0];\n}\n\n// Resolve a column-stack into the concrete list + active index per level.\nfunction buildColumns(\n  root: ActionDropdownNode[],\n  cols: number[]\n): { items: ActionDropdownNode[]; active: number }[] {\n  const result: { items: ActionDropdownNode[]; active: number }[] = [];\n  let level = root;\n  for (let d = 0; d < cols.length; d += 1) {\n    if (!level.length) break;\n    const active = Math.max(0, Math.min(level.length - 1, cols[d] ?? 0));\n    result.push({ items: level, active });\n    const node = level[active];\n    if (!node?.children?.length) break;\n    level = node.children;\n  }\n  if (!result.length) result.push({ items: root, active: 0 });\n  return result;\n}\n\n// Motion spec — see animations.dev easing blueprint. Submenus enter/exit the\n// viewport → ease-out; springs only for the highlight + tactile bits.\nconst EASE_OUT = [0.22, 1, 0.36, 1] as const;\nconst PANEL_SPRING = { type: \"spring\", duration: 0.34, bounce: 0.16 } as const;\nconst PANEL_EXIT = { duration: 0.13, ease: EASE_OUT } as const;\nconst HIGHLIGHT_SPRING = {\n  type: \"spring\",\n  duration: 0.28,\n  bounce: 0.2,\n} as const;\n\n// Beside-the-row submenu sizing, used for viewport collision checks.\nconst SUBMENU_W = 256; // w-64\nconst GAP = 8; // ml-2 / mr-2\ntype SubmenuSide = \"right\" | \"left\" | \"below\";\n\n// useLayoutEffect on the client (no flash), useEffect on the server.\nconst useIsoLayoutEffect =\n  typeof window !== \"undefined\" ? useLayoutEffect : useEffect;\n\nexport function NativeActionDropdown({\n  items,\n  value,\n  defaultValue,\n  onValueChange,\n  label = \"Choose an option\",\n  description,\n  placeholder = \"Select…\",\n  className,\n}: NativeActionDropdownProps) {\n  const isControlled = value !== undefined;\n  const [internalValue, setInternalValue] = useState(defaultValue ?? \"\");\n  const selectedId = isControlled ? value : internalValue;\n\n  const [isOpen, setIsOpen] = useState(false);\n  const [cols, setCols] = useState<number[]>([0]);\n  // Collision-aware placement (recomputed on open / drill / resize).\n  const [submenuSide, setSubmenuSide] = useState<SubmenuSide>(\"right\");\n  const [panelAbove, setPanelAbove] = useState(false);\n\n  const listboxId = useId();\n  const shouldReduceMotion = useReducedMotion();\n  const reduce = shouldReduceMotion ?? false;\n\n  const triggerRef = useRef<HTMLButtonElement>(null);\n  const listboxRef = useRef<HTMLDivElement>(null);\n\n  // Root sections in first-seen order; flat root list keeps that order.\n  const groupKeys = useMemo(() => {\n    const keys: string[] = [];\n    for (const item of items) {\n      const key = item.group ?? \"\";\n      if (!keys.includes(key)) keys.push(key);\n    }\n    return keys;\n  }, [items]);\n  const hasGroups = groupKeys.length > 1 || (groupKeys[0] ?? \"\") !== \"\";\n  const orderedModes = useMemo(\n    () =>\n      groupKeys.flatMap((key) => items.filter((m) => (m.group ?? \"\") === key)),\n    [groupKeys, items]\n  );\n\n  const selection = resolveSelection(items, selectedId, placeholder);\n  const optionId = useCallback(\n    (id: string) => `${listboxId}-${id}`,\n    [listboxId]\n  );\n\n  const columns = useMemo(\n    () => buildColumns(orderedModes, cols),\n    [orderedModes, cols]\n  );\n  const base = useMemo(() => columns.map((column) => column.active), [columns]);\n  const depth = columns.length - 1;\n  const activeNode = columns[depth].items[base[depth]];\n\n  // Ids on the path to the current value — flags which branches hold it.\n  const selectedPath = useMemo(\n    () => new Set((findChain(items, selectedId) ?? []).map((n) => n.id)),\n    [items, selectedId]\n  );\n\n  // Cols for opening a root item's submenu: jump straight to the selected item\n  // when it lives in this subtree, otherwise open its first level.\n  const colsForRoot = useCallback(\n    (rootIndex: number): number[] => {\n      const chain = findChain(items, selectedId);\n      if (\n        chain &&\n        chain.length > 1 &&\n        chain[0].id === orderedModes[rootIndex]?.id\n      ) {\n        return colsForSelection(orderedModes, items, selectedId);\n      }\n      return [rootIndex, 0];\n    },\n    [items, orderedModes, selectedId]\n  );\n\n  const open = useCallback(() => {\n    const chain = findChain(items, selectedId);\n    const rootIdx = chain\n      ? orderedModes.findIndex((mode) => mode.id === chain[0].id)\n      : 0;\n    setCols([rootIdx < 0 ? 0 : rootIdx]);\n    setIsOpen(true);\n  }, [items, orderedModes, selectedId]);\n\n  const close = useCallback((returnFocus = true) => {\n    setIsOpen(false);\n    if (returnFocus) triggerRef.current?.focus();\n  }, []);\n\n  const handleSelect = useCallback(\n    (id: string) => {\n      if (!isControlled) setInternalValue(id);\n      onValueChange?.(id);\n      close();\n    },\n    [close, isControlled, onValueChange]\n  );\n\n  useEffect(() => {\n    if (isOpen) listboxRef.current?.focus();\n  }, [isOpen]);\n\n  // Keep the panel and submenu inside the viewport: flip the panel above the\n  // trigger near the bottom, and open the submenu left / below when there's no\n  // room on the right (full-width \"below\" doubles as the small-screen layout).\n  const computePlacement = useCallback(() => {\n    if (typeof window === \"undefined\") return;\n    const vw = window.innerWidth;\n    const vh = window.innerHeight;\n\n    const trigger = triggerRef.current?.getBoundingClientRect();\n    if (trigger) {\n      const roomBelow = vh - trigger.bottom;\n      const roomAbove = trigger.top;\n      setPanelAbove(\n        roomBelow < Math.min(360, roomAbove) && roomAbove > roomBelow\n      );\n    }\n\n    const panel = listboxRef.current?.getBoundingClientRect();\n    if (panel) {\n      const need = SUBMENU_W + GAP;\n      if (vw < 480 || (vw - panel.right < need && panel.left < need)) {\n        setSubmenuSide(\"below\");\n      } else if (vw - panel.right >= need) {\n        setSubmenuSide(\"right\");\n      } else {\n        setSubmenuSide(\"left\");\n      }\n    }\n  }, []);\n\n  useIsoLayoutEffect(() => {\n    if (!isOpen) return;\n    computePlacement();\n    window.addEventListener(\"resize\", computePlacement);\n    return () => window.removeEventListener(\"resize\", computePlacement);\n  }, [isOpen, depth, computePlacement]);\n\n  const handleTriggerKeyDown = (event: React.KeyboardEvent) => {\n    if (event.key === \"ArrowDown\" || event.key === \"ArrowUp\") {\n      event.preventDefault();\n      open();\n    }\n  };\n\n  const handleListboxKeyDown = (event: React.KeyboardEvent) => {\n    const last = depth;\n    const levelItems = columns[last].items;\n\n    switch (event.key) {\n      case \"ArrowDown\":\n        event.preventDefault();\n        setCols([\n          ...base.slice(0, last),\n          Math.min(levelItems.length - 1, base[last] + 1),\n        ]);\n        break;\n      case \"ArrowUp\":\n        event.preventDefault();\n        setCols([...base.slice(0, last), Math.max(0, base[last] - 1)]);\n        break;\n      case \"Home\":\n        event.preventDefault();\n        setCols([...base.slice(0, last), 0]);\n        break;\n      case \"End\":\n        event.preventDefault();\n        setCols([...base.slice(0, last), levelItems.length - 1]);\n        break;\n      case \"ArrowRight\":\n        if (activeNode?.children?.length) {\n          event.preventDefault();\n          setCols([...base, 0]);\n        }\n        break;\n      case \"ArrowLeft\":\n        if (depth > 0) {\n          event.preventDefault();\n          setCols(base.slice(0, -1));\n        }\n        break;\n      case \"Enter\":\n      case \" \":\n        event.preventDefault();\n        if (activeNode?.children?.length) setCols([...base, 0]);\n        else if (activeNode) handleSelect(activeNode.id);\n        break;\n      case \"Escape\":\n        event.preventDefault();\n        if (depth > 0) setCols(base.slice(0, -1));\n        else close();\n        break;\n      case \"Tab\":\n        close(false);\n        break;\n      default:\n        break;\n    }\n  };\n\n  // Click drives everything: a parent opens its submenu (the root jumps\n  // straight to the selected item if it lives in that subtree); a leaf selects.\n  const handleClick = (columnIndex: number, itemIndex: number) => {\n    const node = columns[columnIndex].items[itemIndex];\n    if (node?.children?.length) {\n      setCols(\n        columnIndex === 0\n          ? colsForRoot(itemIndex)\n          : [...base.slice(0, columnIndex), itemIndex, 0]\n      );\n    } else if (node) {\n      handleSelect(node.id);\n    }\n  };\n\n  const renderOption = (\n    node: ActionDropdownNode,\n    columnIndex: number,\n    itemIndex: number,\n    staggerIndex?: number\n  ) => {\n    const hasChildren = (node.children?.length ?? 0) > 0;\n    const isSelected = selectedId === node.id;\n    const isActive = base[columnIndex] === itemIndex;\n    const isOpenParent =\n      isActive && hasChildren && columns.length > columnIndex + 1;\n    const onSelectedPath = hasChildren && selectedPath.has(node.id);\n    const staggered = staggerIndex !== undefined && !reduce;\n\n    return (\n      <motion.button\n        id={optionId(node.id)}\n        type=\"button\"\n        role={hasChildren ? \"menuitem\" : \"menuitemradio\"}\n        aria-haspopup={hasChildren ? \"menu\" : undefined}\n        aria-expanded={hasChildren ? isOpenParent : undefined}\n        aria-checked={hasChildren ? undefined : isSelected}\n        tabIndex={-1}\n        onClick={() => handleClick(columnIndex, itemIndex)}\n        whileTap={reduce ? undefined : { scale: 0.99 }}\n        initial={staggered ? { opacity: 0, y: 3 } : false}\n        animate={staggered ? { opacity: 1, y: 0 } : undefined}\n        transition={\n          staggered\n            ? {\n                delay: 0.04 + staggerIndex * 0.03,\n                duration: 0.16,\n                ease: EASE_OUT,\n              }\n            : undefined\n        }\n        className=\"group relative block w-full cursor-pointer rounded-lg px-2.5 py-2 text-left outline-none transition-colors hover:bg-accent/40\"\n      >\n        {isActive ? (\n          <motion.span\n            layoutId={`${listboxId}-hl-${columnIndex}`}\n            aria-hidden=\"true\"\n            transition={reduce ? { duration: 0 } : HIGHLIGHT_SPRING}\n            className=\"pointer-events-none absolute inset-0 rounded-lg bg-accent\"\n          />\n        ) : null}\n\n        <span className=\"relative z-10 flex items-start gap-2\">\n          <span className=\"min-w-0 flex-1\">\n            <span className=\"flex items-center gap-2\">\n              <span className=\"truncate text-sm font-medium text-foreground\">\n                {node.name}\n              </span>\n              {node.meta ? (\n                <span className=\"shrink-0 rounded-full border border-border bg-background px-1.5 py-px text-[10px] font-medium leading-4 text-muted-foreground\">\n                  {node.meta}\n                </span>\n              ) : null}\n            </span>\n            {node.detail ? (\n              <span className=\"mt-0.5 block text-[11px] leading-4 text-muted-foreground\">\n                {node.detail}\n              </span>\n            ) : null}\n          </span>\n\n          <span className=\"mt-0.5 flex h-5 min-w-5 shrink-0 items-center justify-end gap-1\">\n            {hasChildren ? (\n              <>\n                {onSelectedPath ? (\n                  <span\n                    className=\"h-1.5 w-1.5 rounded-full bg-primary\"\n                    aria-hidden=\"true\"\n                  />\n                ) : null}\n                <ChevronRight\n                  className={cn(\n                    \"h-4 w-4 transition-colors\",\n                    isOpenParent ? \"text-foreground\" : \"text-muted-foreground\"\n                  )}\n                  aria-hidden=\"true\"\n                />\n              </>\n            ) : (\n              <AnimatePresence initial={false}>\n                {isSelected ? (\n                  <motion.span\n                    key=\"check\"\n                    initial={reduce ? false : { opacity: 0, scale: 0.5 }}\n                    animate={{ opacity: 1, scale: 1 }}\n                    exit={reduce ? undefined : { opacity: 0, scale: 0.5 }}\n                    transition={\n                      reduce\n                        ? { duration: 0 }\n                        : { type: \"spring\", duration: 0.3, bounce: 0.45 }\n                    }\n                    className=\"text-primary\"\n                  >\n                    <Check className=\"h-4 w-4\" aria-hidden=\"true\" />\n                  </motion.span>\n                ) : null}\n              </AnimatePresence>\n            )}\n          </span>\n        </span>\n      </motion.button>\n    );\n  };\n\n  // One bounded submenu beside the open root row. It drills in place; the path\n  // is shown as a breadcrumb (not stacked cards), so depth never widens it.\n  // On small screens (\"below\") it renders IN FLOW as an accordion so it pushes\n  // the remaining rows down instead of covering them.\n  const renderSubmenu = () => {\n    const parent =\n      depth > 0 ? columns[depth - 1].items[base[depth - 1]] : undefined;\n    const isInline = submenuSide === \"below\";\n\n    if (isInline) {\n      return (\n        <motion.div\n          key=\"submenu\"\n          role=\"group\"\n          aria-label={parent ? `${parent.name} options` : \"Submenu\"}\n          initial={reduce ? { opacity: 0 } : { opacity: 0, height: 0 }}\n          animate={reduce ? { opacity: 1 } : { opacity: 1, height: \"auto\" }}\n          exit={\n            reduce\n              ? { opacity: 0, transition: { duration: 0 } }\n              : {\n                  opacity: 0,\n                  height: 0,\n                  transition: { duration: 0.14, ease: EASE_OUT },\n                }\n          }\n          transition={\n            reduce ? { duration: 0 } : { duration: 0.18, ease: EASE_OUT }\n          }\n          className=\"overflow-hidden\"\n        >\n          <div className=\"mt-1 w-full rounded-xl border border-border bg-popover p-1 text-popover-foreground shadow-sm ring-1 ring-black/[0.02]\">\n            {renderSubmenuInner()}\n          </div>\n        </motion.div>\n      );\n    }\n\n    const sideClass =\n      submenuSide === \"right\"\n        ? \"left-full top-0 ml-2 w-64\"\n        : \"right-full top-0 mr-2 w-64\";\n    const sideOrigin = submenuSide === \"right\" ? \"left top\" : \"right top\";\n    const enterX = reduce ? 0 : submenuSide === \"left\" ? 6 : -6;\n    return (\n      <motion.div\n        key=\"submenu\"\n        role=\"group\"\n        aria-label={parent ? `${parent.name} options` : \"Submenu\"}\n        initial={{ opacity: 0, x: enterX, scale: reduce ? 1 : 0.98 }}\n        animate={{ opacity: 1, x: 0, scale: 1 }}\n        exit={{\n          opacity: 0,\n          x: enterX === 0 ? 0 : enterX > 0 ? 4 : -4,\n          scale: reduce ? 1 : 0.98,\n          transition: reduce\n            ? { duration: 0 }\n            : { duration: 0.1, ease: EASE_OUT },\n        }}\n        transition={\n          reduce ? { duration: 0 } : { duration: 0.14, ease: EASE_OUT }\n        }\n        style={{ transformOrigin: sideOrigin }}\n        className={cn(\n          \"absolute z-50 rounded-xl border border-border bg-popover p-1 text-popover-foreground shadow-xl ring-1 ring-black/[0.02]\",\n          sideClass\n        )}\n      >\n        {renderSubmenuInner()}\n      </motion.div>\n    );\n  };\n\n  const renderSubmenuInner = () => (\n    <>\n      <div className=\"flex flex-wrap items-center gap-x-0.5 gap-y-0.5 px-1.5 pb-1.5 pt-1\">\n          <AnimatePresence initial={false} mode=\"popLayout\">\n            {Array.from({ length: depth }).map((_, level) => {\n              const crumb = columns[level].items[base[level]];\n              const isLast = level === depth - 1;\n              return (\n                <motion.span\n                  key={crumb?.id ?? level}\n                  initial={reduce ? false : { opacity: 0, x: -4 }}\n                  animate={{ opacity: 1, x: 0 }}\n                  exit={\n                    reduce\n                      ? { opacity: 0 }\n                      : { opacity: 0, x: -4, transition: { duration: 0.1 } }\n                  }\n                  transition={\n                    reduce\n                      ? { duration: 0 }\n                      : { duration: 0.14, ease: EASE_OUT }\n                  }\n                  className=\"flex items-center gap-x-0.5\"\n                >\n                  {level > 0 ? (\n                    <ChevronRight\n                      className=\"h-3 w-3 shrink-0 text-muted-foreground/50\"\n                      aria-hidden=\"true\"\n                    />\n                  ) : null}\n                  {isLast ? (\n                    <span className=\"truncate text-[11px] font-semibold text-foreground\">\n                      {crumb?.name}\n                    </span>\n                  ) : (\n                    <button\n                      type=\"button\"\n                      role=\"menuitem\"\n                      tabIndex={-1}\n                      aria-label={`Go to ${crumb?.name ?? \"\"}`}\n                      onClick={() => setCols(base.slice(0, level + 2))}\n                      className=\"truncate rounded text-[11px] font-medium text-muted-foreground outline-none transition-colors hover:text-foreground focus-visible:text-foreground\"\n                    >\n                      {crumb?.name}\n                    </button>\n                  )}\n                </motion.span>\n              );\n            })}\n          </AnimatePresence>\n        </div>\n\n        <div className=\"relative max-h-[min(60vh,18rem)] overflow-y-auto border-t border-border pt-1\">\n          <AnimatePresence mode=\"popLayout\" initial={false}>\n            <motion.div\n              key={depth}\n              initial={{ opacity: 0 }}\n              animate={{ opacity: 1 }}\n              exit={{\n                opacity: 0,\n                transition: { duration: reduce ? 0 : 0.08, ease: EASE_OUT },\n              }}\n              transition={\n                reduce ? { duration: 0 } : { duration: 0.12, ease: EASE_OUT }\n              }\n            >\n              {columns[depth].items.map((node, itemIndex) =>\n                renderOption(node, depth, itemIndex, itemIndex)\n              )}\n            </motion.div>\n          </AnimatePresence>\n        </div>\n    </>\n  );\n\n  const renderRootRow = (mode: ActionDropdownNode, flatIndex: number) => {\n    const submenuOpen =\n      base[0] === flatIndex && depth >= 1 && (mode.children?.length ?? 0) > 0;\n    return (\n      <div key={mode.id} className=\"relative\">\n        {renderOption(mode, 0, flatIndex, flatIndex)}\n        <AnimatePresence>\n          {submenuOpen ? renderSubmenu() : null}\n        </AnimatePresence>\n      </div>\n    );\n  };\n\n  return (\n    <div className={cn(\"relative w-full max-w-xs\", className)}>\n      <motion.button\n        ref={triggerRef}\n        type=\"button\"\n        onClick={() => (isOpen ? close(false) : open())}\n        onKeyDown={handleTriggerKeyDown}\n        whileTap={reduce ? undefined : { scale: 0.985 }}\n        transition={{ duration: 0.12, ease: EASE_OUT }}\n        aria-expanded={isOpen}\n        aria-haspopup=\"menu\"\n        aria-controls={isOpen ? listboxId : undefined}\n        className=\"flex w-full items-center gap-3 cursor-pointer rounded-lg border border-border bg-card px-4 py-2.5 text-left shadow-sm transition-colors hover:bg-accent/40 focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-ring focus-visible:ring-offset-2 focus-visible:ring-offset-background\"\n      >\n        <span className=\"min-w-0 flex-1\">\n          <span className=\"block truncate text-sm font-medium\">\n            {selection.name}\n          </span>\n          {selection.summary ? (\n            <span className=\"mt-0.5 block truncate text-[11px] leading-4 text-muted-foreground\">\n              {selection.summary}\n            </span>\n          ) : null}\n        </span>\n        <motion.span\n          aria-hidden=\"true\"\n          animate={reduce ? undefined : { rotate: isOpen ? 180 : 0 }}\n          transition={{ duration: 0.18, ease: EASE_OUT }}\n          className=\"shrink-0 text-muted-foreground\"\n        >\n          <ChevronDown className=\"h-4 w-4\" />\n        </motion.span>\n      </motion.button>\n\n      <AnimatePresence>\n        {isOpen ? (\n          <>\n            <button\n              type=\"button\"\n              aria-label=\"Close dropdown\"\n              tabIndex={-1}\n              className=\"fixed inset-0 z-40 cursor-default bg-transparent\"\n              onClick={() => close()}\n            />\n\n            <motion.div\n              ref={listboxRef}\n              id={listboxId}\n              role=\"menu\"\n              tabIndex={-1}\n              aria-label={label}\n              aria-activedescendant={\n                activeNode ? optionId(activeNode.id) : undefined\n              }\n              onKeyDown={handleListboxKeyDown}\n              initial={{\n                opacity: 0,\n                y: reduce ? 0 : panelAbove ? 6 : -6,\n                scale: reduce ? 1 : 0.97,\n              }}\n              animate={{ opacity: 1, y: 0, scale: 1 }}\n              exit={{\n                opacity: 0,\n                y: reduce ? 0 : panelAbove ? 4 : -4,\n                scale: reduce ? 1 : 0.98,\n                transition: reduce ? { duration: 0 } : PANEL_EXIT,\n              }}\n              transition={reduce ? { duration: 0 } : PANEL_SPRING}\n              style={{\n                transformOrigin: panelAbove ? \"bottom center\" : \"top center\",\n              }}\n              className={cn(\n                \"absolute left-0 z-50 w-full rounded-xl border border-border bg-popover p-1.5 text-popover-foreground shadow-xl outline-none ring-1 ring-black/[0.02]\",\n                panelAbove\n                  ? \"bottom-full mb-2 origin-bottom\"\n                  : \"top-full mt-2 origin-top\"\n              )}\n            >\n              <div role=\"none\" className=\"px-2 pb-1.5 pt-2\">\n                <p className=\"text-sm font-medium\">{label}</p>\n                {description ? (\n                  <p className=\"mt-0.5 text-[11px] leading-4 text-muted-foreground\">\n                    {description}\n                  </p>\n                ) : null}\n              </div>\n\n              {hasGroups ? (\n                groupKeys.map((group) => (\n                  <div\n                    key={group || \"ungrouped\"}\n                    role=\"group\"\n                    aria-label={group || undefined}\n                    className=\"pb-1 pt-1.5\"\n                  >\n                    {group ? (\n                      <div\n                        aria-hidden=\"true\"\n                        className=\"px-2 pb-1 text-[10px] font-medium uppercase tracking-[0.14em] text-muted-foreground\"\n                      >\n                        {group}\n                      </div>\n                    ) : null}\n                    {orderedModes\n                      .map((mode, flatIndex) => ({ mode, flatIndex }))\n                      .filter(({ mode }) => (mode.group ?? \"\") === group)\n                      .map(({ mode, flatIndex }) =>\n                        renderRootRow(mode, flatIndex)\n                      )}\n                  </div>\n                ))\n              ) : (\n                <div role=\"none\" className=\"pb-1 pt-1\">\n                  {orderedModes.map((mode, flatIndex) =>\n                    renderRootRow(mode, flatIndex)\n                  )}\n                </div>\n              )}\n\n              <div\n                role=\"none\"\n                className=\"mt-1 flex items-center justify-between border-t border-border px-2 pb-1 pt-2 text-[11px] leading-4 text-muted-foreground\"\n              >\n                <span className=\"truncate\">\n                  Current:{\" \"}\n                  <span className=\"font-medium text-foreground\">\n                    {selection.name}\n                  </span>\n                </span>\n                <span\n                  aria-hidden=\"true\"\n                  className=\"hidden shrink-0 items-center gap-1 sm:flex\"\n                >\n                  {[\"↑↓\", \"←\", \"→\", \"↵\"].map((key) => (\n                    <kbd\n                      key={key}\n                      className=\"rounded border border-border bg-background px-1 font-sans text-[10px]\"\n                    >\n                      {key}\n                    </kbd>\n                  ))}\n                </span>\n              </div>\n            </motion.div>\n          </>\n        ) : null}\n      </AnimatePresence>\n    </div>\n  );\n}\n",
      "type": "registry:component",
      "target": "components/uitripled/native-action-dropdown-shadcnui.tsx"
    }
  ]
}