{
  "$schema": "https://ui.shadcn.com/schema/registry-item.json",
  "name": "accordion",
  "title": "Accordion",
  "description": "Stacked rows that open in place, so a long page of answers stays short until someone asks for one.",
  "dependencies": [
    "radix-ui",
    "motion",
    "lucide-react"
  ],
  "registryDependencies": [
    "utils",
    "https://miseui.junsu4621.workers.dev/r/motion.json"
  ],
  "files": [
    {
      "path": "registry/miseui/ui/accordion.tsx",
      "content": "import type * as React from \"react\"\nimport { createContext, useContext, useMemo, useState } from \"react\"\nimport { Accordion as AccordionPrimitive } from \"radix-ui\"\nimport { motion, useReducedMotion } from \"motion/react\"\nimport { ChevronDown, Plus } from \"lucide-react\"\nimport { SPRING_DEFAULT } from \"@/registry/miseui/lib/motion\"\nimport {\n  ITEM_CLASS,\n  PANEL_CLASS,\n  ROOT_CLASS,\n  TRIGGER_CLASS,\n  toOpenList,\n  type AccordionEffect,\n  type AccordionIconName,\n  type AccordionProps,\n  type AccordionValue,\n  type AccordionVariant,\n} from \"@/registry/miseui/ui/accordion-classes\"\nimport { cn } from \"@/registry/miseui/lib/utils\"\n\ninterface AccordionContextValue {\n  variant: AccordionVariant\n  effect: AccordionEffect | undefined\n  icon: AccordionIconName\n  openList: readonly string[]\n  reduceMotion: boolean\n}\n\nconst AccordionContext = createContext<AccordionContextValue>({\n  variant: \"plain\",\n  effect: undefined,\n  icon: \"chevron\",\n  openList: [],\n  reduceMotion: false,\n})\nconst ItemOpenContext = createContext(false)\n\nconst INSTANT = { duration: 0 } as const\n/** A chevron flips to point at what it will do; a plus turns into a close mark. */\nconst ICON_TURN: Record<AccordionIconName, number> = { chevron: 180, plus: 45 }\n\nfunction Accordion({\n  variant = \"plain\",\n  effect,\n  icon,\n  className,\n  type = \"single\",\n  collapsible = true,\n  value,\n  defaultValue,\n  onValueChange,\n  ...rest\n}: AccordionProps) {\n  const reduceMotion = useReducedMotion() ?? false\n  const [uncontrolled, setUncontrolled] = useState<AccordionValue | undefined>(defaultValue)\n  const openList = useMemo(() => toOpenList(value ?? uncontrolled), [value, uncontrolled])\n  const resolvedIcon = icon ?? (variant === \"flush\" ? \"plus\" : \"chevron\")\n  const context = useMemo(\n    () => ({ variant, effect, icon: resolvedIcon, openList, reduceMotion }),\n    [variant, effect, resolvedIcon, openList, reduceMotion],\n  )\n  const onChange = (next: AccordionValue) => {\n    setUncontrolled(next)\n    ;(onValueChange as ((value: AccordionValue) => void) | undefined)?.(next)\n  }\n  const shared = {\n    ...rest,\n    \"data-slot\": \"accordion\",\n    \"data-variant\": variant,\n    \"data-effect\": effect,\n    className: cn(ROOT_CLASS[variant], className),\n    onValueChange: onChange,\n  }\n  return (\n    <AccordionContext.Provider value={context}>\n      {type === \"multiple\" ? (\n        <AccordionPrimitive.Root {...shared} type=\"multiple\" value={openList as string[]} />\n      ) : (\n        <AccordionPrimitive.Root {...shared} type=\"single\" collapsible={collapsible} value={openList[0] ?? \"\"} />\n      )}\n    </AccordionContext.Provider>\n  )\n}\n\nfunction AccordionItem({ className, value, ...props }: React.ComponentProps<typeof AccordionPrimitive.Item>) {\n  const { variant, openList } = useContext(AccordionContext)\n  return (\n    <ItemOpenContext.Provider value={openList.includes(value)}>\n      <AccordionPrimitive.Item\n        data-slot=\"accordion-item\"\n        value={value}\n        className={cn(ITEM_CLASS[variant], className)}\n        {...props}\n      />\n    </ItemOpenContext.Provider>\n  )\n}\n\nfunction AccordionIcon() {\n  const { effect, icon, reduceMotion } = useContext(AccordionContext)\n  const open = useContext(ItemOpenContext)\n  const Icon = icon === \"plus\" ? Plus : ChevronDown\n  if (effect === \"spring\") {\n    return (\n      <motion.span\n        aria-hidden=\"true\"\n        data-slot=\"accordion-icon\"\n        data-icon={icon}\n        className=\"flex shrink-0 text-muted-foreground\"\n        initial={false}\n        animate={{ rotate: open ? ICON_TURN[icon] : 0 }}\n        transition={reduceMotion ? INSTANT : SPRING_DEFAULT}\n      >\n        <Icon className=\"size-4\" />\n      </motion.span>\n    )\n  }\n  return (\n    <span aria-hidden=\"true\" data-slot=\"accordion-icon\" data-icon={icon} className=\"flex shrink-0 text-muted-foreground\">\n      <Icon\n        className={cn(\n          \"size-4 transition-transform duration-(--duration-base) ease-(--ease-mise) motion-reduce:transition-none\",\n          icon === \"plus\"\n            ? \"group-data-[state=open]/trigger:rotate-45\"\n            : \"group-data-[state=open]/trigger:rotate-180\",\n        )}\n      />\n    </span>\n  )\n}\n\nfunction AccordionTrigger({ className, children, ...props }: React.ComponentProps<typeof AccordionPrimitive.Trigger>) {\n  const { variant } = useContext(AccordionContext)\n  return (\n    <AccordionPrimitive.Header className=\"flex\" data-slot=\"accordion-header\">\n      <AccordionPrimitive.Trigger\n        data-slot=\"accordion-trigger\"\n        className={cn(\n          \"group/trigger flex w-full items-center justify-between gap-4 rounded-sm text-left outline-none\",\n          \"transition-colors duration-(--duration-fast) ease-(--ease-mise)\",\n          \"focus-visible:ring-[3px] focus-visible:ring-inset focus-visible:ring-ring/50\",\n          \"disabled:pointer-events-none disabled:opacity-50\",\n          TRIGGER_CLASS[variant],\n          className,\n        )}\n        {...props}\n      >\n        {children}\n        <AccordionIcon />\n      </AccordionPrimitive.Trigger>\n    </AccordionPrimitive.Header>\n  )\n}\n\nfunction AccordionContent({ className, children, ...props }: React.ComponentProps<typeof AccordionPrimitive.Content>) {\n  const { variant, effect, reduceMotion } = useContext(AccordionContext)\n  const open = useContext(ItemOpenContext)\n  const panel = <div className={cn(PANEL_CLASS[variant], className)}>{children}</div>\n  if (effect !== \"spring\") {\n    return (\n      <AccordionPrimitive.Content\n        data-slot=\"accordion-content\"\n        className=\"overflow-hidden data-[state=closed]:animate-accordion-up data-[state=open]:animate-accordion-down motion-reduce:animate-none\"\n        {...props}\n      >\n        {panel}\n      </AccordionPrimitive.Content>\n    )\n  }\n  // forceMount keeps the panel in the DOM so the spring can run both ways, so inert is what keeps a closed row out of the tab order.\n  return (\n    <AccordionPrimitive.Content forceMount inert={!open} data-slot=\"accordion-content\" {...props}>\n      <motion.div\n        className=\"overflow-hidden\"\n        initial={false}\n        animate={{ height: open ? \"auto\" : 0 }}\n        transition={reduceMotion ? INSTANT : SPRING_DEFAULT}\n      >\n        {panel}\n      </motion.div>\n    </AccordionPrimitive.Content>\n  )\n}\n\nexport {\n  Accordion,\n  AccordionItem,\n  AccordionTrigger,\n  AccordionContent,\n  type AccordionProps,\n  type AccordionVariant,\n  type AccordionEffect,\n  type AccordionIconName,\n}\n",
      "type": "registry:ui"
    },
    {
      "path": "registry/miseui/ui/accordion-classes.ts",
      "content": "import type * as React from \"react\"\n\nexport type AccordionVariant = \"plain\" | \"card\" | \"separated\" | \"flush\"\nexport type AccordionEffect = \"spring\"\nexport type AccordionIconName = \"chevron\" | \"plus\"\nexport type AccordionValue = string | string[]\n\nexport const ROOT_CLASS: Record<AccordionVariant, string> = {\n  plain: \"w-full\",\n  card: \"w-full divide-y divide-border overflow-hidden rounded-lg border border-border bg-card text-card-foreground\",\n  separated: \"grid w-full gap-2\",\n  flush: \"w-full\",\n}\n\nexport const ITEM_CLASS: Record<AccordionVariant, string> = {\n  plain: \"border-b border-border last:border-b-0\",\n  card: \"\",\n  separated:\n    \"rounded-lg border border-border bg-card text-card-foreground transition-shadow duration-(--duration-base) ease-(--ease-mise) data-[state=open]:shadow-sm\",\n  flush: \"\",\n}\n\nexport const TRIGGER_CLASS: Record<AccordionVariant, string> = {\n  plain: \"min-h-11 py-3 text-sm font-medium hover:text-foreground/70\",\n  card: \"min-h-11 rounded-none px-4 py-3 text-sm font-medium hover:bg-accent/60\",\n  separated: \"min-h-11 rounded-lg px-4 py-3 text-sm font-medium hover:bg-accent/60\",\n  flush: \"min-h-12 py-4 text-lg font-medium tracking-tight hover:text-foreground/70\",\n}\n\nexport const PANEL_CLASS: Record<AccordionVariant, string> = {\n  plain: \"pb-4 text-sm/6 text-muted-foreground\",\n  card: \"px-4 pb-4 text-sm/6 text-muted-foreground\",\n  separated: \"px-4 pb-4 text-sm/6 text-muted-foreground\",\n  flush: \"pb-4 text-[15px]/6 text-muted-foreground\",\n}\n\ninterface AccordionLook {\n  /** `plain` hairlines, `card` one container, `separated` one card per row, `flush` large type with no borders. */\n  variant?: AccordionVariant\n  /** `spring` animates height from wherever it is now, so a second click mid-flight reverses instead of jumping. */\n  effect?: AccordionEffect\n  /** Defaults to `plus` on `flush` and `chevron` elsewhere. */\n  icon?: AccordionIconName\n  disabled?: boolean\n  orientation?: \"horizontal\" | \"vertical\"\n  dir?: \"ltr\" | \"rtl\"\n}\n\ntype AccordionSelection =\n  | {\n      type?: \"single\"\n      /** Whether the open row can be closed again. On by default: a row that cannot close surprises people. */\n      collapsible?: boolean\n      value?: string\n      defaultValue?: string\n      onValueChange?: (value: string) => void\n    }\n  | {\n      type: \"multiple\"\n      collapsible?: never\n      value?: string[]\n      defaultValue?: string[]\n      onValueChange?: (value: string[]) => void\n    }\n\nexport type AccordionProps = Omit<React.ComponentProps<\"div\">, \"defaultValue\" | \"dir\"> & AccordionLook & AccordionSelection\n\nexport function toOpenList(value: AccordionValue | undefined): string[] {\n  if (Array.isArray(value)) return value\n  return value ? [value] : []\n}\n",
      "type": "registry:ui"
    }
  ],
  "categories": [
    "display"
  ],
  "type": "registry:ui"
}