{
  "$schema": "https://ui.shadcn.com/schema/registry-item.json",
  "name": "accordion",
  "title": "Accordion",
  "author": "andongmin94",
  "dependencies": [
    "@base-ui/react"
  ],
  "files": [
    {
      "path": "src/components/ui/accordion.tsx",
      "content": "\"use client\";\n\nimport { Accordion as AccordionPrimitive } from \"@base-ui/react/accordion\";\nimport { DirectionProvider, useDirection } from \"@base-ui/react/direction-provider\";\nimport { mergeProps } from \"@base-ui/react/merge-props\";\nimport * as React from \"react\";\n\nimport { cn } from \"@/lib/utils\";\nimport { ChevronDownIcon, ChevronUpIcon } from \"lucide-react\";\n\ntype AccordionBaseProps = Omit<\n  AccordionPrimitive.Root.Props<string>,\n  \"defaultValue\" | \"loopFocus\" | \"multiple\" | \"onValueChange\" | \"value\"\n> & {\n  asChild?: boolean;\n  children?: React.ReactNode;\n  loop?: boolean;\n  loopFocus?: boolean;\n};\n\ntype AccordionSingleProps = AccordionBaseProps & {\n  collapsible?: boolean;\n  defaultValue?: string;\n  onValueChange?: (value: string, eventDetails: AccordionPrimitive.Root.ChangeEventDetails) => void;\n  type: \"single\";\n  value?: string;\n};\n\ntype AccordionMultipleProps = AccordionBaseProps & {\n  collapsible?: never;\n  defaultValue?: string[];\n  onValueChange?: (\n    value: string[],\n    eventDetails: AccordionPrimitive.Root.ChangeEventDetails,\n  ) => void;\n  type: \"multiple\";\n  value?: string[];\n};\n\ntype AccordionProps = AccordionSingleProps | AccordionMultipleProps;\ntype AccordionKeyDownHandler = NonNullable<AccordionPrimitive.Root.Props<string>[\"onKeyDown\"]>;\ntype RenderProps = React.HTMLAttributes<HTMLElement> & { ref?: React.Ref<HTMLElement> };\ntype AccordionContentStyle = React.CSSProperties & {\n  \"--radix-accordion-content-height\"?: string;\n  \"--radix-accordion-content-width\"?: string;\n};\n\nconst accordionContentCssVariables: AccordionContentStyle = {\n  \"--radix-accordion-content-height\": \"var(--accordion-panel-height)\",\n  \"--radix-accordion-content-width\": \"var(--accordion-panel-width)\",\n};\n\nfunction mergeContentStyle(\n  style: AccordionPrimitive.Panel.Props[\"style\"],\n): AccordionPrimitive.Panel.Props[\"style\"] {\n  if (typeof style === \"function\") {\n    return (state) => ({ ...accordionContentCssVariables, ...style(state) });\n  }\n\n  return { ...accordionContentCssVariables, ...style };\n}\n\nfunction mergeRefs(...refs: (React.Ref<HTMLElement> | undefined)[]) {\n  return (element: HTMLElement | null) => {\n    refs.forEach((ref) => {\n      if (typeof ref === \"function\") {\n        ref(element);\n      } else if (ref) {\n        ref.current = element;\n      }\n    });\n  };\n}\n\nfunction preserveRadixEventCancellation(\n  child: React.ReactElement<{ [key: string]: unknown; children?: React.ReactNode }>,\n) {\n  const eventProps: Record<string, unknown> = {};\n\n  for (const [name, handler] of Object.entries(child.props)) {\n    if (/^on[A-Z]/.test(name) && typeof handler === \"function\") {\n      eventProps[name] = (...args: unknown[]) => {\n        (handler as (...handlerArgs: unknown[]) => void)(...args);\n        const event = args[0] as\n          | { defaultPrevented?: boolean; preventBaseUIHandler?: () => void }\n          | undefined;\n        if (event?.defaultPrevented) event.preventBaseUIHandler?.();\n      };\n    }\n  }\n\n  return React.cloneElement(child, eventProps);\n}\n\nfunction renderWithAliases<State>(\n  render:\n    | React.ReactElement\n    | ((props: RenderProps, state: State) => React.ReactElement)\n    | undefined,\n  fallback: React.ReactElement,\n  getAliases: (state: State) => Record<string, string | undefined>,\n) {\n  return (elementProps: RenderProps, state: State) => {\n    const aliasedProps = mergeProps(\n      elementProps as React.ComponentPropsWithRef<\"div\">,\n      getAliases(state) as React.ComponentPropsWithRef<\"div\">,\n    ) as RenderProps;\n\n    if (typeof render === \"function\") {\n      return render(aliasedProps, state);\n    }\n\n    const element = (render ?? fallback) as React.ReactElement<RenderProps>;\n    const mergedProps = mergeProps(\n      aliasedProps as React.ComponentPropsWithRef<\"div\">,\n      element.props as React.ComponentPropsWithRef<\"div\">,\n    ) as RenderProps;\n    mergedProps.ref = mergeRefs(aliasedProps.ref, element.props.ref);\n    return React.cloneElement(element, mergedProps);\n  };\n}\n\nfunction getChildElement(children: React.ReactNode, componentName: string) {\n  const child = React.Children.toArray(children).find(React.isValidElement);\n  if (child === undefined) {\n    throw new Error(`${componentName} with asChild requires a valid React element child.`);\n  }\n  return preserveRadixEventCancellation(\n    child as React.ReactElement<{ [key: string]: unknown; children?: React.ReactNode }>,\n  );\n}\n\nfunction isDisabledAccordionTrigger(trigger: HTMLElement) {\n  return (\n    (trigger instanceof HTMLButtonElement && trigger.disabled) ||\n    trigger.getAttribute(\"aria-disabled\") === \"true\" ||\n    trigger.hasAttribute(\"data-disabled\")\n  );\n}\n\nfunction getAccordionTriggers(root: HTMLElement) {\n  return Array.from(root.querySelectorAll<HTMLElement>('[data-slot=\"accordion-trigger\"]')).filter(\n    (trigger) =>\n      trigger.closest<HTMLElement>('[data-slot=\"accordion\"]') === root &&\n      !isDisabledAccordionTrigger(trigger),\n  );\n}\n\nfunction Accordion(props: AccordionProps) {\n  const inheritedDirection = useDirection();\n  const direction = props.dir === \"rtl\" ? \"rtl\" : props.dir === \"ltr\" ? \"ltr\" : inheritedDirection;\n  const disabled = props.disabled ?? false;\n  const loop = props.loop ?? props.loopFocus ?? true;\n  const orientation = props.orientation ?? \"vertical\";\n  const onKeyDown = props.onKeyDown;\n\n  const handleKeyDown = React.useCallback<AccordionKeyDownHandler>(\n    (event) => {\n      onKeyDown?.(event);\n      if (event.defaultPrevented || event.baseUIHandlerPrevented || disabled) {\n        event.preventBaseUIHandler();\n        return;\n      }\n\n      const navigationKeys = new Set([\n        \"Home\",\n        \"End\",\n        \"ArrowDown\",\n        \"ArrowUp\",\n        \"ArrowLeft\",\n        \"ArrowRight\",\n      ]);\n      if (!navigationKeys.has(event.key)) {\n        return;\n      }\n\n      const target = (event.target as Element).closest<HTMLElement>(\n        '[data-slot=\"accordion-trigger\"]',\n      );\n      const root = event.currentTarget;\n      if (target?.closest<HTMLElement>('[data-slot=\"accordion\"]') !== root) {\n        return;\n      }\n\n      const triggers = getAccordionTriggers(root);\n      const currentIndex = target === null ? -1 : triggers.indexOf(target);\n      if (currentIndex === -1 || triggers.length === 0) {\n        return;\n      }\n\n      event.preventDefault();\n      event.preventBaseUIHandler();\n\n      let nextIndex = currentIndex;\n      const lastIndex = triggers.length - 1;\n      const move = (offset: number) => {\n        const candidate = currentIndex + offset;\n        if (candidate < 0) {\n          nextIndex = loop ? lastIndex : 0;\n        } else if (candidate > lastIndex) {\n          nextIndex = loop ? 0 : lastIndex;\n        } else {\n          nextIndex = candidate;\n        }\n      };\n\n      switch (event.key) {\n        case \"Home\":\n          nextIndex = 0;\n          break;\n        case \"End\":\n          nextIndex = lastIndex;\n          break;\n        case \"ArrowDown\":\n          if (orientation === \"vertical\") move(1);\n          break;\n        case \"ArrowUp\":\n          if (orientation === \"vertical\") move(-1);\n          break;\n        case \"ArrowRight\":\n          if (orientation === \"horizontal\") move(direction === \"rtl\" ? -1 : 1);\n          break;\n        case \"ArrowLeft\":\n          if (orientation === \"horizontal\") move(direction === \"rtl\" ? 1 : -1);\n          break;\n      }\n\n      triggers[nextIndex]?.focus();\n    },\n    [direction, disabled, loop, onKeyDown, orientation],\n  );\n\n  if (props.type === \"multiple\") {\n    const {\n      asChild = false,\n      className,\n      children,\n      defaultValue,\n      loop: _loop,\n      loopFocus: _loopFocus,\n      onKeyDown: _onKeyDown,\n      onValueChange,\n      orientation: _orientation,\n      render,\n      type: _type,\n      value,\n      ...rootProps\n    } = props;\n\n    const accordion = (\n      <AccordionPrimitive.Root<string>\n        data-slot=\"accordion\"\n        data-orientation={orientation}\n        className={cn(\"flex w-full flex-col\", className)}\n        defaultValue={defaultValue}\n        loopFocus={loop}\n        multiple\n        onKeyDown={handleKeyDown}\n        onValueChange={onValueChange}\n        orientation={orientation}\n        render={renderWithAliases<AccordionPrimitive.Root.State<string>>(\n          asChild ? getChildElement(children, \"Accordion\") : render,\n          React.createElement(\"div\"),\n          (state) => ({\n            \"data-disabled\": state.disabled ? \"\" : undefined,\n            \"data-orientation\": state.orientation,\n          }),\n        )}\n        value={value}\n        {...rootProps}\n      >\n        {asChild ? undefined : children}\n      </AccordionPrimitive.Root>\n    );\n\n    return <DirectionProvider direction={direction}>{accordion}</DirectionProvider>;\n  }\n\n  const {\n    asChild = false,\n    className,\n    collapsible = false,\n    children,\n    defaultValue,\n    loop: _loop,\n    loopFocus: _loopFocus,\n    onKeyDown: _onKeyDown,\n    onValueChange,\n    orientation: _orientation,\n    render,\n    type: _type,\n    value,\n    ...rootProps\n  } = props;\n\n  const accordion = (\n    <AccordionPrimitive.Root<string>\n      data-slot=\"accordion\"\n      data-orientation={orientation}\n      className={cn(\"flex w-full flex-col\", className)}\n      defaultValue={defaultValue === undefined ? undefined : [defaultValue]}\n      loopFocus={loop}\n      multiple={false}\n      onKeyDown={handleKeyDown}\n      onValueChange={(nextValue, eventDetails) => {\n        if (!collapsible && nextValue.length === 0) {\n          eventDetails.cancel();\n          return;\n        }\n\n        onValueChange?.(nextValue[0] ?? \"\", eventDetails);\n      }}\n      orientation={orientation}\n      render={renderWithAliases<AccordionPrimitive.Root.State<string>>(\n        asChild ? getChildElement(children, \"Accordion\") : render,\n        React.createElement(\"div\"),\n        (state) => ({\n          \"data-disabled\": state.disabled ? \"\" : undefined,\n          \"data-orientation\": state.orientation,\n        }),\n      )}\n      value={value === undefined ? undefined : [value]}\n      {...rootProps}\n    >\n      {asChild ? undefined : children}\n    </AccordionPrimitive.Root>\n  );\n\n  return <DirectionProvider direction={direction}>{accordion}</DirectionProvider>;\n}\n\nfunction AccordionItem({\n  asChild = false,\n  children,\n  className,\n  render,\n  ...props\n}: AccordionPrimitive.Item.Props & { asChild?: boolean; children?: React.ReactNode }) {\n  return (\n    <AccordionPrimitive.Item\n      data-slot=\"accordion-item\"\n      className={cn(\n        \"overflow-hidden rounded-base border-2 border-b border-border shadow-shadow\",\n        className,\n      )}\n      render={renderWithAliases<AccordionPrimitive.Item.State>(\n        asChild ? getChildElement(children, \"AccordionItem\") : render,\n        React.createElement(\"div\"),\n        (state) => ({\n          \"data-disabled\": state.disabled ? \"\" : undefined,\n          \"data-orientation\": state.orientation,\n          \"data-state\": state.open ? \"open\" : \"closed\",\n        }),\n      )}\n      {...props}\n    >\n      {asChild ? undefined : children}\n    </AccordionPrimitive.Item>\n  );\n}\n\nfunction AccordionTrigger({\n  asChild = false,\n  className,\n  children,\n  render,\n  ...props\n}: AccordionPrimitive.Trigger.Props & { asChild?: boolean; children?: React.ReactNode }) {\n  return (\n    <AccordionPrimitive.Header\n      className=\"flex\"\n      render={renderWithAliases<AccordionPrimitive.Item.State>(\n        undefined,\n        React.createElement(\"h3\"),\n        (state) => ({\n          \"data-disabled\": state.disabled ? \"\" : undefined,\n          \"data-orientation\": state.orientation,\n          \"data-state\": state.open ? \"open\" : \"closed\",\n        }),\n      )}\n    >\n      <AccordionPrimitive.Trigger\n        data-slot=\"accordion-trigger\"\n        className={cn(\n          \"group/accordion-trigger flex flex-1 items-center justify-between border-border bg-main p-4 text-left text-base font-heading text-main-foreground transition-all focus-visible:ring-[3px] aria-disabled:pointer-events-none aria-disabled:opacity-50 data-disabled:pointer-events-none data-disabled:opacity-50 data-panel-open:rounded-b-none data-panel-open:border-b-2 **:data-[slot=accordion-trigger-icon]:ml-auto **:data-[slot=accordion-trigger-icon]:size-5\",\n          className,\n        )}\n        render={renderWithAliases<AccordionPrimitive.Trigger.State>(\n          asChild ? getChildElement(children, \"AccordionTrigger\") : render,\n          React.createElement(\"button\"),\n          (state) => ({\n            \"data-disabled\": state.disabled ? \"\" : undefined,\n            \"data-orientation\": state.orientation,\n            \"data-state\": state.open ? \"open\" : \"closed\",\n          }),\n        )}\n        {...props}\n      >\n        {asChild ? undefined : children}\n        <ChevronDownIcon\n          data-slot=\"accordion-trigger-icon\"\n          className=\"pointer-events-none shrink-0 transition-transform duration-200 group-data-panel-open/accordion-trigger:rotate-180 group-aria-expanded/accordion-trigger:rotate-180\"\n        />\n        <ChevronUpIcon\n          data-slot=\"accordion-trigger-icon\"\n          className=\"pointer-events-none hidden shrink-0\"\n        />\n      </AccordionPrimitive.Trigger>\n    </AccordionPrimitive.Header>\n  );\n}\n\nfunction AccordionContent({\n  asChild = false,\n  children,\n  className,\n  forceMount,\n  keepMounted,\n  render,\n  style,\n  ...props\n}: Omit<AccordionPrimitive.Panel.Props, \"keepMounted\"> & {\n  asChild?: boolean;\n  children?: React.ReactNode;\n  forceMount?: true;\n  keepMounted?: boolean;\n}) {\n  const child = asChild ? getChildElement(children, \"AccordionContent\") : undefined;\n  const content = child ? (child.props as { children?: React.ReactNode }).children : children;\n  const contentElement = (\n    <div\n      className={cn(\n        \"p-4 [&_a]:underline [&_a]:underline-offset-3 [&_a]:hover:text-foreground [&_p:not(:last-child)]:mb-4\",\n        className,\n      )}\n    >\n      {content}\n    </div>\n  );\n\n  return (\n    <AccordionPrimitive.Panel\n      data-slot=\"accordion-content\"\n      className=\"h-(--accordion-panel-height) overflow-hidden rounded-b-base bg-secondary-background text-sm font-base transition-[height] duration-200 ease-out data-ending-style:h-0 data-starting-style:h-0\"\n      keepMounted={keepMounted ?? forceMount}\n      style={mergeContentStyle(style)}\n      render={renderWithAliases<AccordionPrimitive.Panel.State>(\n        child ? React.cloneElement(child, undefined, contentElement) : render,\n        React.createElement(\"div\"),\n        (state) => ({\n          \"data-disabled\": state.disabled ? \"\" : undefined,\n          \"data-orientation\": state.orientation,\n          \"data-state\": state.open ? \"open\" : \"closed\",\n        }),\n      )}\n      {...props}\n    >\n      {child ? undefined : contentElement}\n    </AccordionPrimitive.Panel>\n  );\n}\n\nexport { Accordion, AccordionItem, AccordionTrigger, AccordionContent };\n",
      "type": "registry:ui"
    }
  ],
  "type": "registry:ui"
}