import React from 'react' ; import { createContextScope } from '@radix-ui/react-context' ; import { createCollection } from '@radix-ui/react-collection' ; import { useComposedRefs } from '@radix-ui/react-compose-refs' ; import { composeEventHandlers } from '@radix-ui/primitive' ; import { useControllableState } from '@radix-ui/react-use-controllable-state' ; import { Primitive } from '@radix-ui/react-primitive' ; import * as CollapsiblePrimitive from '@radix-ui/react-collapsible' ; import { createCollapsibleScope } from '@radix-ui/react-collapsible' ; 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 ACCORDION_NAME = 'Accordion' ; const ACCORDION_KEYS = [ 'Home' , 'End' , 'ArrowDown' , 'ArrowUp' ] ; const [ Collection , useCollection , createCollectionScope ] = createCollection < AccordionTriggerElement > ( ACCORDION_NAME ) ; type ScopedProps < P > = P & { __scopeAccordion ? : Scope ; } ; const [ createAccordionContext , createAccordionScope ] = createContextScope ( ACCORDION_NAME , [ createCollectionScope , createCollapsibleScope , ] ) ; const useCollapsibleScope = createCollapsibleScope ( ) ; type AccordionElement = AccordionImplMultipleElement | AccordionImplSingleElement ; interface AccordionSingleProps extends AccordionImplSingleProps { type : 'single' ; } interface AccordionMultipleProps extends AccordionImplMultipleProps { type : 'multiple' ; } const Accordion = React . forwardRef < AccordionElement , AccordionSingleProps | AccordionMultipleProps > ( ( props , forwardedRef ) => { const { type , ... accordionProps } = props ; const singleProps = accordionProps as AccordionImplSingleProps ; const multipleProps = accordionProps as AccordionImplMultipleProps ; return ( < Collection . Provider scope = { props . __scopeAccordion } > { type === 'multiple' ? ( < AccordionImplMultiple { ... multipleProps } ref = { forwardedRef } / > ) : ( < AccordionImplSingle { ... singleProps } ref = { forwardedRef } / > ) } < /Collection.Provider>); } ) ; Accordion . displayName = ACCORDION_NAME ; Accordion . propTypes = { type ( props ) { const value = props . value || props . defaultValue ; if ( props . type && ! [ 'single' , 'multiple' ] . includes ( props . type ) ) { return new Error ( 'Invalid prop `type` supplied to `Accordion`. Expected one of `single | multiple`.' ) ; } if ( props . type === 'multiple' && typeof value === 'string' ) { return new Error ( 'Invalid prop `type` supplied to `Accordion`. Expected `single` when `defaultValue` or `value` is type `string`.' ) ; } if ( props . type === 'single' && Array . isArray ( value ) ) { return new Error ( 'Invalid prop `type` supplied to `Accordion`. Expected `multiple` when `defaultValue` or `value` is type `string[]`.' ) ; } return null ; } , } ; type AccordionValueContextValue = { value : string [ ] ; onItemOpen ( value ) : void ; onItemClose ( value ) : void ; } ; const [ AccordionValueProvider , useAccordionValueContext ] = createAccordionContext < AccordionValueContextValue > ( ACCORDION_NAME ) ; const [ AccordionCollapsibleProvider , useAccordionCollapsibleContext ] = createAccordionContext ( ACCORDION_NAME , { collapsible : false } ) ; type AccordionImplSingleElement = AccordionImplElement ; interface AccordionImplSingleProps extends AccordionImplProps { /**
     * The controlled stateful value of the accordion item whose content is expanded.
     */ value ? : string ; /**
     * The value of the item whose content is expanded when the accordion is initially rendered. Use
     * `defaultValue` if you do not need to control the state of an accordion.
     */ defaultValue ? : string ; /**
     * The callback that fires when the state of the accordion changes.
     */ onValueChange ? ( value ) : void ; default collapsible ? : boolean ; } const AccordionImplSingle = React . forwardRef < AccordionImplSingleElement , AccordionImplSingleProps > ( ( props , forwardedRef ) => { const { value : valueProp , defaultValue , onValueChange = ( ) => { } , collapsible = false , ... accordionSingleProps } = props ; const [ value , setValue ] = useControllableState ( { prop : valueProp , defaultProp : defaultValue , onChange : onValueChange , } ) ; return ( < AccordionValueProvider scope = { props . __scopeAccordion } value = { value ? [ value ] : [ ] } onItemOpen = { setValue } onItemClose = { React . useCallback ( ( ) => collapsible && setValue ( '' ) , [ collapsible , setValue ] ) } > < AccordionCollapsibleProvider scope = { props . __scopeAccordion } collapsible = { collapsible } > < AccordionImpl { ... accordionSingleProps } ref = { forwardedRef } / > < /AccordionCollapsibleProvider> < /AccordionValueProvider>); } ) ; type AccordionImplMultipleElement = AccordionImplElement ; interface AccordionImplMultipleProps extends AccordionImplProps { /**
     * The controlled stateful value of the accordion items whose contents are expanded.
     */ value ? : string [ ] ; /**
     * The value of the items whose contents are expanded when the accordion is initially rendered. Use
     * `defaultValue` if you do not need to control the state of an accordion.
     */ defaultValue ? : string [ ] ; /**
     * The callback that fires when the state of the accordion changes.
     */ onValueChange ? ( value ) : void ; } const AccordionImplMultiple = React . forwardRef < AccordionImplMultipleElement , AccordionImplMultipleProps > ( ( props , forwardedRef ) => { const { value : valueProp , defaultValue , onValueChange = ( ) => { } , ... accordionMultipleProps } = props ; const [ value = [ ] , setValue ] = useControllableState ( { prop : valueProp , defaultProp : defaultValue , onChange : onValueChange , } ) ; const handleItemOpen = React . useCallback ( ( itemValue ) => setValue ( ( prevValue = [ ] ) => [ ... prevValue , itemValue ] ) , [ setValue ] ) ; const handleItemClose = React . useCallback ( ( itemValue ) => setValue ( ( prevValue = [ ] ) => prevValue . filter ( ( value ) => value !== itemValue ) ) , [ setValue ] ) ; return ( < AccordionValueProvider scope = { props . __scopeAccordion } value = { value } onItemOpen = { handleItemOpen } onItemClose = { handleItemClose } > < AccordionCollapsibleProvider scope = { props . __scopeAccordion } collapsible = { true } > < AccordionImpl { ... accordionMultipleProps } ref = { forwardedRef } / > < /AccordionCollapsibleProvider> < /AccordionValueProvider>); } ) ; type AccordionImplContextValue = { disabled ? : boolean ; } ; const [ AccordionImplProvider , useAccordionContext ] = createAccordionContext < AccordionImplContextValue > ( ACCORDION_NAME ) ; type AccordionImplElement = React . ElementRef < typeof Primitive . div > ; type PrimitiveDivProps = Radix . ComponentPropsWithoutRef < typeof Primitive . div > ; interface AccordionImplProps extends PrimitiveDivProps { defaultValue disabled ? : boolean ; } const AccordionImpl = React . forwardRef < AccordionImplElement , AccordionImplProps > ( ( props , forwardedRef ) => { const { __scopeAccordion , disabled , ... accordionProps } = props ; const accordionRef = React . useRef < AccordionImplElement > ( null ) ; const composedRefs = useComposedRefs ( accordionRef , forwardedRef ) ; const getItems = useCollection ( __scopeAccordion ) ; const handleKeyDown = composeEventHandlers ( props . onKeyDown , ( event ) => { if ( ! ACCORDION_KEYS . includes ( event . key ) ) return ; const target = event . target as HTMLElement ; const triggerCollection = getItems ( ) . filter ( ( item ) => ! item . ref . current ?. disabled ) ; const triggerIndex = triggerCollection . findIndex ( ( item ) => item . ref . current === target ) ; const triggerCount = triggerCollection . length ; if ( triggerIndex === - 1 ) return ; event . preventDefault ( ) ; let nextIndex = triggerIndex ; switch ( event . key ) { case 'Home' : nextIndex = 0 ; break ; case 'End' : nextIndex = triggerCount - 1 ; break ; case 'ArrowDown' : nextIndex = triggerIndex + 1 ; break ; case 'ArrowUp' : nextIndex = triggerIndex - 1 ; if ( nextIndex < 0 ) { nextIndex = triggerCount - 1 ; } break ; } const clampedIndex = nextIndex % triggerCount ; triggerCollection [ clampedIndex ] . ref . current ?. focus ( ) ; } ) ; return ( < AccordionImplProvider scope = { __scopeAccordion } disabled = { disabled } > < Collection . Slot scope = { __scopeAccordion } > < Primitive . div { ... accordionProps } ref = { composedRefs } onKeyDown = { disabled ? undefined : handleKeyDown } / > < /Collection.Slot> < /AccordionImplProvider>); } ) ; const ITEM_NAME = 'AccordionItem' ; type AccordionItemContextValue = { open ? : boolean ; disabled ? : boolean ; triggerId : string ; } ; const [ AccordionItemProvider , useAccordionItemContext ] = createAccordionContext < AccordionItemContextValue > ( ITEM_NAME ) ; type AccordionItemElement = React . ElementRef < typeof CollapsiblePrimitive . Root > ; type CollapsibleProps = Radix . ComponentPropsWithoutRef < typeof CollapsiblePrimitive . Root > ; interface AccordionItemProps extends Omit < CollapsibleProps , 'open' | 'defaultOpen' | 'onOpenChange' > { defaultValue disabled ? : boolean ; /**
     * A string value for the accordion item. All items within an accordion should use a unique value.
     */ value : string ; } /**
 * `AccordionItem` contains all of the parts of a collapsible section inside of an `Accordion`.
 */ const AccordionItem = React . forwardRef < AccordionItemElement , AccordionItemProps > ( ( props , forwardedRef ) => { const { __scopeAccordion , value , ... accordionItemProps } = props ; const accordionContext = useAccordionContext ( ITEM_NAME , __scopeAccordion ) ; const valueContext = useAccordionValueContext ( ITEM_NAME , __scopeAccordion ) ; const collapsibleScope = useCollapsibleScope ( __scopeAccordion ) ; const triggerId = useId ( ) ; const open = ( value && valueContext . value . includes ( value ) ) || false ; const disabled = accordionContext . disabled || props . disabled ; return ( < AccordionItemProvider scope = { __scopeAccordion } open = { open } disabled = { disabled } triggerId = { triggerId } > < CollapsiblePrimitive . Root data - state = { open ? 'open' : 'closed' } { ... collapsibleScope } { ... accordionItemProps } ref = { forwardedRef } disabled = { disabled } open = { open } onOpenChange = { ( open ) => { if ( open ) { valueContext . onItemOpen ( value ) ; } else { valueContext . onItemClose ( value ) ; } } } /> < /AccordionItemProvider>); } ) ; AccordionItem . displayName = ITEM_NAME ; const HEADER_NAME = 'AccordionHeader' ; type AccordionHeaderElement = React . ElementRef < typeof Primitive . h3 > ; type PrimitiveHeading3Props = Radix . ComponentPropsWithoutRef < typeof Primitive . h3 > ; interface AccordionHeaderProps extends PrimitiveHeading3Props { } /**
 * `AccordionHeader` contains the content for the parts of an `AccordionItem` that will be visible
 * whether or not its content is collapsed.
 */ const AccordionHeader = React . forwardRef < AccordionHeaderElement , AccordionHeaderProps > ( ( props , forwardedRef ) => { const { __scopeAccordion , ... headerProps } = props ; const itemContext = useAccordionItemContext ( HEADER_NAME , __scopeAccordion ) ; return ( < Primitive . h3 data - state = { getState ( itemContext . open ) } data - disabled = { itemContext . disabled ? '' : undefined } { ... headerProps } ref = { forwardedRef } / > ) ; } ) ; AccordionHeader . displayName = HEADER_NAME ; const TRIGGER_NAME = 'AccordionTrigger' ; type AccordionTriggerElement = React . ElementRef < typeof CollapsiblePrimitive . Trigger > ; type CollapsibleTriggerProps = Radix . ComponentPropsWithoutRef < typeof CollapsiblePrimitive . Trigger > ; interface AccordionTriggerProps extends CollapsibleTriggerProps { } /**
 * `AccordionTrigger` is the trigger that toggles the collapsed state of an `AccordionItem`. It
 * should always be nested inside of an `AccordionHeader`.
 */ const AccordionTrigger = React . forwardRef < AccordionTriggerElement , AccordionTriggerProps > ( ( props , forwardedRef ) => { const { __scopeAccordion , ... triggerProps } = props ; const itemContext = useAccordionItemContext ( TRIGGER_NAME , __scopeAccordion ) ; const collapsibleContext = useAccordionCollapsibleContext ( TRIGGER_NAME , __scopeAccordion ) ; const collapsibleScope = useCollapsibleScope ( __scopeAccordion ) ; return ( < Collection . ItemSlot scope = { __scopeAccordion } > < CollapsiblePrimitive . Trigger aria - disabled = { ( itemContext . open && ! collapsibleContext . collapsible ) || undefined } id = { itemContext . triggerId } { ... collapsibleScope } { ... triggerProps } ref = { forwardedRef } / > < /Collection.ItemSlot>); } ) ; AccordionTrigger . displayName = TRIGGER_NAME ; const CONTENT_NAME = 'AccordionContent' ; type AccordionContentElement = React . ElementRef < typeof CollapsiblePrimitive . Content > ; type CollapsibleContentProps = Radix . ComponentPropsWithoutRef < typeof CollapsiblePrimitive . Content > ; interface AccordionContentProps extends CollapsibleContentProps { } /**
 * `AccordionContent` contains the collapsible content for an `AccordionItem`.
 */ const AccordionContent = React . forwardRef < AccordionContentElement , AccordionContentProps > ( ( props , forwardedRef ) => { const { __scopeAccordion , ... contentProps } = props ; const itemContext = useAccordionItemContext ( CONTENT_NAME , __scopeAccordion ) ; const collapsibleScope = useCollapsibleScope ( __scopeAccordion ) ; return ( < CollapsiblePrimitive . Content role = "region" aria - labelledby = { itemContext . triggerId } { ... collapsibleScope } { ... contentProps } ref = { forwardedRef } style = { { [ '--radix-accordion-content-height' as any ] : 'var(--radix-collapsible-content-height)' , [ '--radix-accordion-content-width' as any ] : 'var(--radix-collapsible-content-width)' , ... props . style , } } />); } ) ; AccordionContent . displayName = CONTENT_NAME ; function getState ( open ? ) { return open ? 'open' : 'closed' ; } const Root = Accordion ; const Item = AccordionItem ; const Header = AccordionHeader ; const Trigger = AccordionTrigger ; const Content = AccordionContent ; export { createAccordionScope , Accordion , AccordionItem , AccordionHeader , AccordionTrigger , AccordionContent , Root , Item , Header , Trigger , Content , } ; export type { AccordionSingleProps , AccordionMultipleProps , AccordionItemProps , AccordionHeaderProps , AccordionTriggerProps , AccordionContentProps , } ;