import * as React from 'react' ; import { composeEventHandlers } from '@radix-ui/primitive' ; import { createContextScope } from '@radix-ui/react-context' ; import { useControllableState } from '@radix-ui/react-use-controllable-state' ; import { useLayoutEffect } from '@radix-ui/react-use-layout-effect' ; import { useComposedRefs } from '@radix-ui/react-compose-refs' ; import { Primitive } from '@radix-ui/react-primitive' ; import { Presence } from '@radix-ui/react-presence' ; import { useId } from '@radix-ui/react-id' ; import type * as Radix from '@radix-ui/react-primitive' ; import type { Scope } from '@radix-ui/react-context' ; const COLLAPSIBLE_NAME = 'Collapsible' ; type ScopedProps < P > = P & { __scopeCollapsible ? : Scope ; } ; const [ createCollapsibleContext , createCollapsibleScope ] = createContextScope ( COLLAPSIBLE_NAME ) ; type CollapsibleContextValue = { contentId : string ; disabled ? : boolean ; open : boolean ; onOpenToggle ( ) : void ; } ; const [ CollapsibleProvider , useCollapsibleContext ] = createCollapsibleContext < CollapsibleContextValue > ( COLLAPSIBLE_NAME ) ; type CollapsibleElement = React . ElementRef < typeof Primitive . div > ; type PrimitiveDivProps = Radix . ComponentPropsWithoutRef < typeof Primitive . div > ; interface CollapsibleProps extends PrimitiveDivProps { defaultOpen ? : boolean ; open ? : boolean ; disabled ? : boolean ; onOpenChange ? ( open ) : void ; } const Collapsible = React . forwardRef < CollapsibleElement , CollapsibleProps > ( ( props , forwardedRef ) => { const { __scopeCollapsible , open : openProp , defaultOpen , disabled , onOpenChange , ... collapsibleProps } = props ; const [ open = false , setOpen ] = useControllableState ( { prop : openProp , defaultProp : defaultOpen , onChange : onOpenChange , } ) ; return ( < CollapsibleProvider scope = { __scopeCollapsible } disabled = { disabled } contentId = { useId ( ) } open = { open } onOpenToggle = { React . useCallback ( ( ) => setOpen ( ( prevOpen ) => ! prevOpen ) , [ setOpen ] ) } > < Primitive . div data - state = { getState ( open ) } data - disabled = { disabled ? '' : undefined } { ... collapsibleProps } ref = { forwardedRef } / > < /CollapsibleProvider>); } ) ; Collapsible . displayName = COLLAPSIBLE_NAME ; const TRIGGER_NAME = 'CollapsibleTrigger' ; type CollapsibleTriggerElement = React . ElementRef < typeof Primitive . button > ; type PrimitiveButtonProps = Radix . ComponentPropsWithoutRef < typeof Primitive . button > ; interface CollapsibleTriggerProps extends PrimitiveButtonProps { } const CollapsibleTrigger = React . forwardRef < CollapsibleTriggerElement , CollapsibleTriggerProps > ( ( props , forwardedRef ) => { const { __scopeCollapsible , ... triggerProps } = props ; const context = useCollapsibleContext ( TRIGGER_NAME , __scopeCollapsible ) ; return ( < Primitive . button aria - controls = { context . contentId } aria - expanded = { context . open || false } data - state = { getState ( context . open ) } data - disabled = { context . disabled ? '' : undefined } disabled = { context . disabled } { ... triggerProps } ref = { forwardedRef } onClick = { composeEventHandlers ( props . onClick , context . onOpenToggle ) } / > ) ; } ) ; CollapsibleTrigger . displayName = TRIGGER_NAME ; const CONTENT_NAME = 'CollapsibleContent' ; type CollapsibleContentElement = CollapsibleContentImplElement ; interface CollapsibleContentProps extends Omit < CollapsibleContentImplProps , 'present' > { /**
     * Used to force mounting when more control is needed. Useful when
     * controlling animation with React animation libraries.
     */ forceMount ? : true ; } const CollapsibleContent = React . forwardRef < CollapsibleContentElement , CollapsibleContentProps > ( ( props , forwardedRef ) => { const { forceMount , ... contentProps } = props ; const context = useCollapsibleContext ( CONTENT_NAME , props . __scopeCollapsible ) ; return ( < Presence present = { forceMount || context . open } > { ( { present } ) => ( < CollapsibleContentImpl { ... contentProps } ref = { forwardedRef } present = { present } / > ) } < /Presence>); } ) ; CollapsibleContent . displayName = CONTENT_NAME ; type CollapsibleContentImplElement = React . ElementRef < typeof Primitive . div > ; interface CollapsibleContentImplProps extends PrimitiveDivProps { present : boolean ; } const CollapsibleContentImpl = React . forwardRef < CollapsibleContentImplElement , CollapsibleContentImplProps > ( ( props , forwardedRef ) => { const { __scopeCollapsible , present , children , ... contentProps } = props ; const context = useCollapsibleContext ( CONTENT_NAME , __scopeCollapsible ) ; const [ isPresent , setIsPresent ] = React . useState ( present ) ; const ref = React . useRef < CollapsibleContentImplElement > ( null ) ; const composedRefs = useComposedRefs ( forwardedRef , ref ) ; const heightRef = React . useRef < number | undefined > ( 0 ) ; const height = heightRef . current ; const widthRef = React . useRef < number | undefined > ( 0 ) ; const width = widthRef . current ; const isOpen = context . open || isPresent ; const isMountAnimationPreventedRef = React . useRef ( isOpen ) ; const originalStylesRef = React . useRef < Record < string , string > > ( ) ; React . useEffect ( ( ) => { const rAF = requestAnimationFrame ( ( ) => ( isMountAnimationPreventedRef . current = false ) ) ; return ( ) => cancelAnimationFrame ( rAF ) ; } , [ ] ) ; useLayoutEffect ( ( ) => { const node = ref . current ; if ( node ) { originalStylesRef . current = originalStylesRef . current || { transitionDuration : node . style . transitionDuration , animationDuration : node . style . animationDuration , } ; node . style . transitionDuration = '0s' ; node . style . animationDuration = '0s' ; const rect = node . getBoundingClientRect ( ) ; heightRef . current = rect . height ; widthRef . current = rect . width ; if ( ! isMountAnimationPreventedRef . current ) { node . style . transitionDuration = originalStylesRef . current . transitionDuration ; node . style . animationDuration = originalStylesRef . current . animationDuration ; } setIsPresent ( present ) ; } } , [ context . open , present ] ) ; return ( < Primitive . div data - state = { getState ( context . open ) } data - disabled = { context . disabled ? '' : undefined } id = { context . contentId } hidden = { ! isOpen } { ... contentProps } ref = { composedRefs } style = { { [ `--radix-collapsible-content-height` as any ] : height ? `${ height }px` : undefined , [ `--radix-collapsible-content-width` as any ] : width ? `${ width }px` : undefined , ... props . style , } } > { isOpen && children } < /Primitive.div>); } ) ; function getState ( open ? ) { return open ? 'open' : 'closed' ; } const Root = Collapsible ; const Trigger = CollapsibleTrigger ; const Content = CollapsibleContent ; export { createCollapsibleScope , Collapsible , CollapsibleTrigger , CollapsibleContent , Root , Trigger , Content , } ; export type { CollapsibleProps , CollapsibleTriggerProps , CollapsibleContentProps } ;