{
  "$schema": "https://ui.shadcn.com/schema/registry-item.json",
  "name": "b-collapsible",
  "type": "registry:ui",
  "registryDependencies": [],
  "dependencies": ["@base-ui/react", "motion", "lucide-react"],
  "devDependencies": [],
  "files": [
    {
      "path": "b-collapsible.tsx",
      "content": "\"use client\";\n\nimport { Collapsible as CollapsiblePrimitive } from \"@base-ui/react/collapsible\";\nimport { ChevronDown } from \"lucide-react\";\nimport { motion, type Transition, useReducedMotion } from \"motion/react\";\nimport * as React from \"react\";\n\nimport { cn } from \"@/lib/utils\";\n\nconst componentThemeClassName =\n  \"[--ic-background:#ffffff] [--ic-foreground:#111111] [--ic-primary:#111111] [--ic-secondary:#646b75] [--ic-surface-border:#e9edf2] [--ic-border:#e3e7ec] [--ic-card:#ffffff] [--ic-card-foreground:#111111] [--ic-muted:#f5f7fa] [--ic-muted-foreground:#6d7480] [--ic-accent:#f3f5f8] [--color-accent:var(--ic-accent)] [--color-accent-foreground:var(--ic-accent-foreground)] [--ic-accent-foreground:#111111] [--ic-input:#e3e7ec] [--ic-ring:rgba(17,17,17,0.16)] [--ic-destructive:#dc2626] [--ic-paper:#fcfcfd] [--ic-popover-foreground:#111111] [--ic-brand:#0ea5e9] [--ic-brand-soft:#bae6fd] [--ic-shadow-soft:0_18px_38px_-24px_rgba(15,23,42,0.35)] [--ic-chart-1:oklch(0.52_0.19_254)] [--ic-chart-2:oklch(0.74_0.11_232)] [--ic-chart-3:oklch(0.42_0.16_262)] [--ic-chart-4:oklch(0.84_0.07_228)] [--ic-chart-5:oklch(0.62_0.14_240)] [--color-background:var(--ic-background)] [--color-foreground:var(--ic-foreground)] [--color-primary:var(--ic-primary)] [--color-secondary:var(--ic-secondary)] [--color-border:var(--ic-border)] [--color-card:var(--ic-card)] [--color-card-foreground:var(--ic-card-foreground)] [--color-muted:var(--ic-muted)] [--color-muted-foreground:var(--ic-muted-foreground)] [--color-accent:var(--ic-accent)] [--color-accent-foreground:var(--ic-accent-foreground)] [--color-input:var(--ic-input)] [--color-ring:var(--ic-ring)] [--color-destructive:var(--ic-destructive)] [--color-paper:var(--ic-paper)] [--color-popover-foreground:var(--ic-popover-foreground)] [--color-brand:var(--ic-brand)] [--color-brand-soft:var(--ic-brand-soft)] [--color-chart-1:var(--ic-chart-1)] [--color-chart-2:var(--ic-chart-2)] [--color-chart-3:var(--ic-chart-3)] [--color-chart-4:var(--ic-chart-4)] [--color-chart-5:var(--ic-chart-5)] dark:[--ic-background:#111111] dark:[--ic-foreground:#f6f3ec] dark:[--ic-primary:#f6f3ec] dark:[--ic-secondary:#cbc6bb] dark:[--ic-surface-border:#2a2a25] dark:[--ic-border:#2b2a25] dark:[--ic-card:#111111] dark:[--ic-card-foreground:#f6f3ec] dark:[--ic-muted:#171716] dark:[--ic-muted-foreground:#9a958a] dark:[--ic-accent:#1a1a18] [--color-accent:var(--ic-accent)] [--color-accent-foreground:var(--ic-accent-foreground)] dark:[--ic-accent-foreground:#f6f3ec] dark:[--ic-input:#2b2a25] dark:[--ic-ring:rgba(246,243,236,0.18)] dark:[--ic-destructive:#f87171] dark:[--ic-paper:#171716] dark:[--ic-popover-foreground:#f6f3ec] dark:[--ic-brand:#38bdf8] dark:[--ic-brand-soft:#0c4a6e] dark:[--ic-shadow-soft:0_20px_44px_-28px_rgba(0,0,0,0.6)] dark:[--ic-chart-1:oklch(0.68_0.17_250)] dark:[--ic-chart-2:oklch(0.82_0.09_225)] dark:[--ic-chart-3:oklch(0.58_0.15_260)] dark:[--ic-chart-4:oklch(0.75_0.12_235)] dark:[--ic-chart-5:oklch(0.88_0.06_220)]\";\n\ntype ButtonHTMLAttributesForMotion = Omit<\n  React.ButtonHTMLAttributes<HTMLButtonElement>,\n  | \"onAnimationEnd\"\n  | \"onAnimationIteration\"\n  | \"onAnimationStart\"\n  | \"onDrag\"\n  | \"onDragEnd\"\n  | \"onDragEnter\"\n  | \"onDragExit\"\n  | \"onDragLeave\"\n  | \"onDragOver\"\n  | \"onDragStart\"\n  | \"onDrop\"\n>;\n\ntype DivHTMLAttributesForMotion = Omit<\n  React.HTMLAttributes<HTMLDivElement>,\n  | \"onAnimationEnd\"\n  | \"onAnimationIteration\"\n  | \"onAnimationStart\"\n  | \"onDrag\"\n  | \"onDragEnd\"\n  | \"onDragEnter\"\n  | \"onDragExit\"\n  | \"onDragLeave\"\n  | \"onDragOver\"\n  | \"onDragStart\"\n  | \"onDrop\"\n>;\n\ntype TriggerRenderProps = React.ButtonHTMLAttributes<HTMLButtonElement> & {\n  children?: React.ReactNode;\n  className?: string;\n  ref?: React.Ref<HTMLButtonElement>;\n  style?: React.CSSProperties;\n};\n\ntype PanelRenderProps = React.HTMLAttributes<HTMLDivElement> & {\n  children?: React.ReactNode;\n  className?: string;\n  ref?: React.Ref<HTMLDivElement>;\n  style?: React.CSSProperties;\n};\n\ntype CollapsibleContextValue = {\n  disabled?: boolean;\n  open: boolean;\n  reduceMotion: boolean;\n};\n\nconst CollapsibleContext = React.createContext<CollapsibleContextValue | null>(\n  null\n);\n\nconst triggerPressTransition = {\n  type: \"spring\" as const,\n  stiffness: 420,\n  damping: 30,\n  mass: 0.7,\n};\n\nconst expandSpring = {\n  type: \"spring\" as const,\n  stiffness: 150,\n  damping: 26,\n  mass: 1.05,\n};\n\nconst collapseSpring = {\n  type: \"spring\" as const,\n  stiffness: 190,\n  damping: 30,\n  mass: 1.1,\n};\n\nconst contentEase = [0.16, 1, 0.3, 1] as const;\n\nconst iconTransition = {\n  type: \"spring\" as const,\n  stiffness: 360,\n  damping: 24,\n  mass: 0.66,\n};\n\nconst instantTransition: Transition = { duration: 0.01 };\n\nconst defaultContentCopyClassName =\n  \"space-y-3 pr-8 pb-3 text-sm leading-relaxed text-muted-foreground [&_a]:font-medium [&_a]:underline [&_a]:underline-offset-4 [&_li+li]:mt-1.5 [&_ol]:mt-3 [&_ol]:list-decimal [&_ol]:pl-5 [&_p+p]:mt-3 [&_ul]:mt-3 [&_ul]:list-disc [&_ul]:pl-5\";\n\nconst defaultTriggerClassName =\n  \"flex w-full items-center justify-between gap-4 py-3 text-left text-foreground transition-[color,opacity] duration-300 ease-[cubic-bezier(0.22,1,0.36,1)] hover:text-foreground/80 focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-ring focus-visible:ring-offset-2 disabled:pointer-events-none disabled:opacity-50\";\n\nfunction setRef<T>(ref: React.Ref<T> | undefined, value: T) {\n  if (typeof ref === \"function\") {\n    ref(value);\n    return;\n  }\n\n  if (ref) {\n    (ref as React.MutableRefObject<T>).current = value;\n  }\n}\n\nfunction mergeRefs<T>(...refs: Array<React.Ref<T> | undefined>) {\n  return (value: T) => {\n    for (const ref of refs) {\n      setRef(ref, value);\n    }\n  };\n}\n\nfunction getSingleChild(\n  children: React.ReactNode,\n  componentName: string\n): React.ReactElement<Record<string, unknown>> {\n  const child = React.Children.only(children);\n\n  if (!React.isValidElement(child)) {\n    throw new Error(\n      `${componentName} with asChild expects a single React element child.`\n    );\n  }\n\n  return child as React.ReactElement<Record<string, unknown>>;\n}\n\nfunction useCollapsibleContext() {\n  const context = React.useContext(CollapsibleContext);\n\n  if (!context) {\n    throw new Error(\"Collapsible parts must be used within Collapsible.\");\n  }\n\n  return context;\n}\n\nfunction getHeightTransition(reduceMotion: boolean, isOpen: boolean) {\n  if (reduceMotion) {\n    return instantTransition;\n  }\n\n  return isOpen ? expandSpring : collapseSpring;\n}\n\nfunction getContentTransition(reduceMotion: boolean, isOpen: boolean) {\n  if (reduceMotion) {\n    return instantTransition;\n  }\n\n  return {\n    opacity: {\n      duration: isOpen ? 0.28 : 0.18,\n      ease: contentEase,\n      delay: isOpen ? 0.04 : 0,\n    },\n    y: isOpen ? expandSpring : collapseSpring,\n  };\n}\n\nfunction resolveTriggerProps(triggerProps: TriggerRenderProps) {\n  const {\n    children: _triggerChildren,\n    className: triggerClassName,\n    onAnimationEnd: _onAnimationEnd,\n    onAnimationIteration: _onAnimationIteration,\n    onAnimationStart: _onAnimationStart,\n    onDrag: _onDrag,\n    onDragEnd: _onDragEnd,\n    onDragEnter: _onDragEnter,\n    onDragExit: _onDragExit,\n    onDragLeave: _onDragLeave,\n    onDragOver: _onDragOver,\n    onDragStart: _onDragStart,\n    onDrop: _onDrop,\n    ref: triggerRef,\n    style: triggerStyle,\n    ...resolvedTriggerProps\n  } = triggerProps;\n\n  return {\n    resolvedTriggerProps,\n    triggerClassName,\n    triggerRef,\n    triggerStyle,\n  };\n}\n\nfunction resolvePanelProps(panelProps: PanelRenderProps) {\n  const {\n    children: _panelChildren,\n    className: panelClassName,\n    hidden: _panelHidden,\n    onAnimationEnd: _onAnimationEnd,\n    onAnimationIteration: _onAnimationIteration,\n    onAnimationStart: _onAnimationStart,\n    onDrag: _onDrag,\n    onDragEnd: _onDragEnd,\n    onDragEnter: _onDragEnter,\n    onDragExit: _onDragExit,\n    onDragLeave: _onDragLeave,\n    onDragOver: _onDragOver,\n    onDragStart: _onDragStart,\n    onDrop: _onDrop,\n    ref: panelRef,\n    style: panelStyle,\n    ...resolvedPanelProps\n  } = panelProps;\n\n  return {\n    panelClassName,\n    panelRef,\n    panelStyle,\n    resolvedPanelProps,\n  };\n}\n\nfunction mergeClickHandlers(\n  ...handlers: Array<React.MouseEventHandler<HTMLButtonElement> | undefined>\n) {\n  return (event: React.MouseEvent<HTMLButtonElement>) => {\n    for (const handler of handlers) {\n      handler?.(event);\n    }\n  };\n}\n\nexport interface CollapsibleProps extends React.HTMLAttributes<HTMLDivElement> {\n  children: React.ReactNode;\n  /** Initial open state for uncontrolled usage. */\n  defaultOpen?: boolean;\n  /** Disables trigger interaction. */\n  disabled?: boolean;\n  /** Called when the open state changes. */\n  onOpenChange?: (open: boolean) => void;\n  /** Controlled open state. */\n  open?: boolean;\n}\n\nconst Collapsible = React.forwardRef<HTMLDivElement, CollapsibleProps>(\n  (\n    {\n      children,\n      className,\n      defaultOpen = false,\n      disabled = false,\n      onOpenChange,\n      open: openProp,\n      ...props\n    },\n    ref\n  ) => {\n    const reduceMotion = useReducedMotion() === true;\n    const isControlled = openProp !== undefined;\n    const [uncontrolledOpen, setUncontrolledOpen] = React.useState(defaultOpen);\n    const open = isControlled ? openProp : uncontrolledOpen;\n\n    const handleOpenChange = React.useCallback(\n      (nextOpen: boolean) => {\n        if (!isControlled) {\n          setUncontrolledOpen(nextOpen);\n        }\n\n        onOpenChange?.(nextOpen);\n      },\n      [isControlled, onOpenChange]\n    );\n\n    const contextValue = React.useMemo(\n      () => ({ disabled, open, reduceMotion }),\n      [disabled, open, reduceMotion]\n    );\n\n    return (\n      <CollapsibleContext.Provider value={contextValue}>\n        <CollapsiblePrimitive.Root\n          {...props}\n          className={cn(\n            componentThemeClassName,\n            \"block w-full min-w-0 overflow-hidden border-border border-b bg-transparent\",\n            className\n          )}\n          disabled={disabled}\n          onOpenChange={handleOpenChange}\n          open={open}\n          ref={ref}\n        >\n          {children}\n        </CollapsiblePrimitive.Root>\n      </CollapsibleContext.Provider>\n    );\n  }\n);\n\nCollapsible.displayName = \"Collapsible\";\n\nexport interface CollapsibleTriggerProps extends ButtonHTMLAttributesForMotion {\n  /** Merge trigger semantics onto the child element instead of rendering a button. */\n  asChild?: boolean;\n  /** Custom indicator node. Defaults to a chevron. */\n  icon?: React.ReactNode;\n  /** Indicator position when using the default trigger layout. */\n  iconPosition?: \"start\" | \"end\";\n  /** Show the built-in indicator. Ignored when `asChild` is true. */\n  showIcon?: boolean;\n}\n\nfunction CollapsibleTriggerIcon({\n  icon,\n  open,\n  reduceMotion,\n}: {\n  icon?: React.ReactNode;\n  open: boolean;\n  reduceMotion: boolean;\n}) {\n  return (\n    <motion.span\n      animate={\n        reduceMotion\n          ? undefined\n          : {\n              rotate: open ? 180 : 0,\n              scale: open ? 1.04 : 1,\n              y: open ? 1 : 0,\n            }\n      }\n      aria-hidden\n      className=\"inline-flex shrink-0 items-center justify-center text-muted-foreground\"\n      transition={iconTransition}\n    >\n      {icon ?? (\n        <ChevronDown aria-hidden className=\"h-4 w-4\" strokeWidth={2.2} />\n      )}\n    </motion.span>\n  );\n}\n\nconst CollapsibleTrigger = React.forwardRef<\n  HTMLButtonElement,\n  CollapsibleTriggerProps\n>(\n  (\n    {\n      asChild = false,\n      children,\n      className,\n      icon,\n      iconPosition = \"end\",\n      onClick,\n      showIcon = true,\n      type = \"button\",\n      ...props\n    },\n    ref\n  ) => {\n    const { disabled, open, reduceMotion } = useCollapsibleContext();\n\n    return (\n      <CollapsiblePrimitive.Trigger\n        nativeButton={!asChild}\n        render={(triggerProps) => {\n          const {\n            resolvedTriggerProps,\n            triggerClassName,\n            triggerRef,\n            triggerStyle,\n          } = resolveTriggerProps(triggerProps);\n\n          if (asChild) {\n            const child = getSingleChild(children, \"CollapsibleTrigger\");\n            const childProps = child.props;\n\n            return React.cloneElement(child, {\n              ...childProps,\n              ...props,\n              className: cn(\n                triggerClassName,\n                childProps.className as string | undefined,\n                className\n              ),\n              disabled: disabled || resolvedTriggerProps.disabled,\n              onClick: mergeClickHandlers(\n                childProps.onClick as\n                  | React.MouseEventHandler<HTMLButtonElement>\n                  | undefined,\n                resolvedTriggerProps.onClick,\n                onClick\n              ),\n              ref: mergeRefs(\n                triggerRef,\n                ref,\n                childProps.ref as React.Ref<HTMLButtonElement> | undefined\n              ),\n              style: {\n                ...(childProps.style as React.CSSProperties),\n                ...triggerStyle,\n              },\n            });\n          }\n\n          const indicator = showIcon ? (\n            <CollapsibleTriggerIcon\n              icon={icon}\n              open={open}\n              reduceMotion={reduceMotion}\n            />\n          ) : null;\n\n          return (\n            <motion.button\n              {...props}\n              {...resolvedTriggerProps}\n              className={cn(\n                defaultTriggerClassName,\n                triggerClassName,\n                className\n              )}\n              disabled={disabled || resolvedTriggerProps.disabled}\n              onClick={mergeClickHandlers(\n                resolvedTriggerProps.onClick,\n                onClick\n              )}\n              ref={mergeRefs(triggerRef, ref)}\n              style={triggerStyle}\n              transition={triggerPressTransition}\n              type={type}\n              whileTap={\n                disabled\n                  ? undefined\n                  : { scale: 0.992, y: 0, transition: triggerPressTransition }\n              }\n            >\n              {showIcon && iconPosition === \"start\" ? indicator : null}\n              <span className=\"min-w-0 flex-1 font-medium text-[15px] leading-6 tracking-[-0.01em]\">\n                {children}\n              </span>\n              {showIcon && iconPosition === \"end\" ? indicator : null}\n            </motion.button>\n          );\n        }}\n      />\n    );\n  }\n);\n\nCollapsibleTrigger.displayName = \"CollapsibleTrigger\";\n\nexport interface CollapsibleContentProps extends DivHTMLAttributesForMotion {\n  /** Merge panel semantics onto the child element instead of rendering animated wrappers. */\n  asChild?: boolean;\n  /** Classes for the inner content wrapper. Ignored when `asChild` is true. */\n  contentClassName?: string;\n  /** Keep content mounted while closed so exit animation can run. */\n  forceMount?: boolean;\n}\n\nconst CollapsibleContent = React.forwardRef<\n  HTMLDivElement,\n  CollapsibleContentProps\n>(\n  (\n    {\n      asChild = false,\n      children,\n      className,\n      contentClassName,\n      forceMount = true,\n      ...props\n    },\n    ref\n  ) => {\n    const { reduceMotion } = useCollapsibleContext();\n\n    return (\n      <CollapsiblePrimitive.Panel\n        keepMounted={forceMount}\n        render={(panelProps, state) => {\n          const isOpen = state.open;\n          const { panelClassName, panelRef, panelStyle, resolvedPanelProps } =\n            resolvePanelProps(panelProps);\n\n          if (asChild) {\n            const child = getSingleChild(children, \"CollapsibleContent\");\n            const childProps = child.props;\n\n            return React.cloneElement(child, {\n              ...childProps,\n              ...props,\n              ...resolvedPanelProps,\n              \"aria-hidden\": !isOpen,\n              className: cn(\n                panelClassName,\n                childProps.className as string | undefined,\n                className\n              ),\n              inert: isOpen ? undefined : true,\n              ref: mergeRefs(\n                panelRef,\n                ref,\n                childProps.ref as React.Ref<HTMLDivElement> | undefined\n              ),\n              style: {\n                ...(childProps.style as React.CSSProperties),\n                ...panelStyle,\n              },\n            });\n          }\n\n          return (\n            <div\n              {...resolvedPanelProps}\n              className={cn(\"overflow-hidden\", panelClassName, className)}\n              ref={panelRef}\n              style={panelStyle}\n            >\n              <motion.div\n                animate={{ height: isOpen ? \"auto\" : 0 }}\n                className=\"overflow-hidden\"\n                initial={false}\n                transition={getHeightTransition(reduceMotion, isOpen)}\n              >\n                <motion.div\n                  {...props}\n                  animate={{\n                    opacity: isOpen ? 1 : 0,\n                    y: isOpen || reduceMotion ? 0 : -4,\n                  }}\n                  aria-hidden={!isOpen}\n                  className={cn(defaultContentCopyClassName, contentClassName)}\n                  inert={isOpen ? undefined : true}\n                  initial={false}\n                  ref={ref}\n                  transition={getContentTransition(reduceMotion, isOpen)}\n                >\n                  {children}\n                </motion.div>\n              </motion.div>\n            </div>\n          );\n        }}\n      />\n    );\n  }\n);\n\nCollapsibleContent.displayName = \"CollapsibleContent\";\n\nexport { Collapsible, CollapsibleContent, CollapsibleTrigger };\n",
      "type": "registry:ui"
    }
  ],
  "title": "Collapsible (Base UI)",
  "description": "Collapsible with the same Iconiq API layered over Base UI primitives, preserving the same height, icon, and content transitions as the Radix version."
}
