{
  "$schema": "https://ui.shadcn.com/schema/registry-item.json",
  "name": "r-collapsible",
  "type": "registry:ui",
  "registryDependencies": [],
  "dependencies": ["@radix-ui/react-collapsible", "motion", "lucide-react"],
  "devDependencies": [],
  "files": [
    {
      "path": "r-collapsible.tsx",
      "content": "\"use client\";\n\nimport * as CollapsiblePrimitive from \"@radix-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 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 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\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      showIcon = true,\n      type = \"button\",\n      ...props\n    },\n    ref\n  ) => {\n    const { disabled, open, reduceMotion } = useCollapsibleContext();\n\n    if (asChild) {\n      return (\n        <CollapsiblePrimitive.Trigger asChild ref={ref} {...props}>\n          {getSingleChild(children, \"CollapsibleTrigger\")}\n        </CollapsiblePrimitive.Trigger>\n      );\n    }\n\n    return (\n      <CollapsiblePrimitive.Trigger asChild>\n        <motion.button\n          className={cn(defaultTriggerClassName, className)}\n          disabled={disabled}\n          ref={ref}\n          transition={triggerPressTransition}\n          type={type}\n          whileTap={\n            disabled\n              ? undefined\n              : { scale: 0.992, y: 0, transition: triggerPressTransition }\n          }\n          {...props}\n        >\n          {showIcon && iconPosition === \"start\" ? (\n            <CollapsibleTriggerIcon\n              icon={icon}\n              open={open}\n              reduceMotion={reduceMotion}\n            />\n          ) : 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\" ? (\n            <CollapsibleTriggerIcon\n              icon={icon}\n              open={open}\n              reduceMotion={reduceMotion}\n            />\n          ) : null}\n        </motion.button>\n      </CollapsiblePrimitive.Trigger>\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\nfunction CollapsibleAnimatedPanel({\n  children,\n  className,\n  contentClassName,\n  isOpen,\n  props,\n  reduceMotion,\n  ref,\n}: {\n  children: React.ReactNode;\n  className?: string;\n  contentClassName?: string;\n  isOpen: boolean;\n  props: DivHTMLAttributesForMotion;\n  reduceMotion: boolean;\n  ref: React.Ref<HTMLDivElement>;\n}) {\n  return (\n    <motion.div\n      animate={{ height: isOpen ? \"auto\" : 0 }}\n      className={cn(\"overflow-hidden\", className)}\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  );\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 { open, reduceMotion } = useCollapsibleContext();\n\n    if (asChild) {\n      return (\n        <CollapsiblePrimitive.Content\n          asChild\n          forceMount={forceMount || undefined}\n          ref={ref}\n          {...props}\n        >\n          {getSingleChild(children, \"CollapsibleContent\")}\n        </CollapsiblePrimitive.Content>\n      );\n    }\n\n    return (\n      <CollapsiblePrimitive.Content\n        asChild\n        forceMount={forceMount || undefined}\n      >\n        <CollapsibleAnimatedPanel\n          className={className}\n          contentClassName={contentClassName}\n          isOpen={open}\n          props={props}\n          reduceMotion={reduceMotion}\n          ref={ref}\n        >\n          {children}\n        </CollapsibleAnimatedPanel>\n      </CollapsiblePrimitive.Content>\n    );\n  }\n);\n\nCollapsibleContent.displayName = \"CollapsibleContent\";\n\nexport { Collapsible, CollapsibleContent, CollapsibleTrigger };\n",
      "type": "registry:ui"
    }
  ],
  "title": "Collapsible (Radix UI)",
  "description": "Collapsible with the same Iconiq API layered over Radix UI primitives, preserving the same height, icon, and content transitions as the Base UI version."
}
