import * as Accordion from '@radix-ui/react-accordion';
useIsomorphicLayoutEffect,
} from '~/components/primitives/hooks';
import * as Slot from '~/components/primitives/slot';
import * as React from 'react';
import { Pressable, View } from 'react-native';
const AccordionContext = React.createContext<RootProps | null>(null);
const Root = React.forwardRef<RootRef, RootProps>(
onValueChange: onValueChangeProps,
orientation = 'vertical',
const [value = type === 'multiple' ? [] : undefined, onValueChange] = useControllableState<
(string | undefined) | string[]
defaultProp: defaultValue,
onChange: onValueChangeProps as (state: string | string[] | undefined) => void,
const Component = asChild ? Slot.View : View;
<AccordionContext.Provider
onValueChange={onValueChange as any}
orientation={orientation}
collapsible={collapsible}
<Component ref={ref} {...props} />
</AccordionContext.Provider>
Root.displayName = 'RootWebAccordion';
function useRootContext() {
const context = React.useContext(AccordionContext);
'Accordion compound components cannot be rendered outside the Accordion component'
const AccordionItemContext = React.createContext<(ItemProps & { isExpanded: boolean }) | null>(
const Item = React.forwardRef<ItemRef, ItemProps>(
({ asChild, value: itemValue, disabled, ...props }, ref) => {
const augmentedRef = useAugmentedRef({ ref });
const { value, orientation, disabled: disabledRoot } = useRootContext();
useIsomorphicLayoutEffect(() => {
if (augmentedRef.current) {
const augRef = augmentedRef.current as unknown as HTMLDivElement;
const isExpanded = Array.isArray(value) ? value.includes(itemValue) : value === itemValue;
augRef.dataset.state = isExpanded ? 'open' : 'closed';
useIsomorphicLayoutEffect(() => {
if (augmentedRef.current) {
const augRef = augmentedRef.current as unknown as HTMLDivElement;
augRef.dataset.orientation = orientation;
if (disabled || disabledRoot) {
augRef.dataset.disabled = 'true';
augRef.dataset.disabled = undefined;
}, [orientation, disabled, disabledRoot]);
const Component = asChild ? Slot.View : View;
<AccordionItemContext.Provider
isExpanded: isItemExpanded(value, itemValue),
<Accordion.Item value={itemValue} disabled={disabled} asChild>
<Component ref={augmentedRef} {...props} />
</AccordionItemContext.Provider>
Item.displayName = 'ItemWebAccordion';
function useItemContext() {
const context = React.useContext(AccordionItemContext);
'AccordionItem compound components cannot be rendered outside the AccordionItem component'
const Header = React.forwardRef<HeaderRef, HeaderProps>(({ asChild, ...props }, ref) => {
const augmentedRef = useAugmentedRef({ ref });
const { disabled, isExpanded } = useItemContext();
const { orientation, disabled: disabledRoot } = useRootContext();
useIsomorphicLayoutEffect(() => {
if (augmentedRef.current) {
const augRef = augmentedRef.current as unknown as HTMLDivElement;
augRef.dataset.state = isExpanded ? 'open' : 'closed';
useIsomorphicLayoutEffect(() => {
if (augmentedRef.current) {
const augRef = augmentedRef.current as unknown as HTMLDivElement;
augRef.dataset.orientation = orientation;
if (disabled || disabledRoot) {
augRef.dataset.disabled = 'true';
augRef.dataset.disabled = undefined;
}, [orientation, disabled, disabledRoot]);
const Component = asChild ? Slot.View : View;
<Accordion.Header asChild>
<Component ref={augmentedRef} {...props} />
Header.displayName = 'HeaderWebAccordion';
const HIDDEN_STYLE: React.CSSProperties = {
const Trigger = React.forwardRef<TriggerRef, TriggerProps>(
({ asChild, disabled: disabledProp, ...props }, ref) => {
const { disabled: disabledRoot } = useRootContext();
const { disabled, isExpanded } = useItemContext();
const triggerRef = React.useRef<HTMLButtonElement>(null);
const augmentedRef = useAugmentedRef({ ref });
useIsomorphicLayoutEffect(() => {
if (augmentedRef.current) {
const augRef = augmentedRef.current as unknown as HTMLDivElement;
augRef.dataset.state = isExpanded ? 'expanded' : 'closed';
useIsomorphicLayoutEffect(() => {
if (augmentedRef.current) {
const augRef = augmentedRef.current as unknown as HTMLDivElement;
if (disabled || disabledRoot || disabledProp) {
augRef.dataset.disabled = 'true';
augRef.dataset.disabled = undefined;
}, [disabled, disabledRoot, disabledProp]);
useIsomorphicLayoutEffect(() => {
if (triggerRef.current) {
triggerRef.current.disabled = true;
const isDisabled = disabledProp ?? disabledRoot ?? disabled;
const Component = asChild ? Slot.Pressable : Pressable;
<Accordion.Trigger ref={triggerRef} aria-hidden tabIndex={-1} style={HIDDEN_STYLE} />
<Accordion.Trigger disabled={isDisabled} asChild>
if (triggerRef.current && !isDisabled) {
triggerRef.current.disabled = false;
triggerRef.current.click();
triggerRef.current.disabled = true;
Trigger.displayName = 'TriggerWebAccordion';
const Content = React.forwardRef<ContentRef, ContentProps>(
({ asChild, forceMount, ...props }, ref) => {
const augmentedRef = useAugmentedRef({ ref });
const { orientation, disabled: disabledRoot } = useRootContext();
const { disabled, isExpanded } = useItemContext();
useIsomorphicLayoutEffect(() => {
if (augmentedRef.current) {
const augRef = augmentedRef.current as unknown as HTMLDivElement;
augRef.dataset.state = isExpanded ? 'expanded' : 'closed';
useIsomorphicLayoutEffect(() => {
if (augmentedRef.current) {
const augRef = augmentedRef.current as unknown as HTMLDivElement;
augRef.dataset.orientation = orientation;
if (disabled || disabledRoot) {
augRef.dataset.disabled = 'true';
augRef.dataset.disabled = undefined;
}, [orientation, disabled, disabledRoot]);
const Component = asChild ? Slot.View : View;
<Accordion.Content forceMount={forceMount} asChild>
<Component ref={augmentedRef} {...props} />
Content.displayName = 'ContentWebAccordion';
export { Content, Header, Item, Root, Trigger, useItemContext, useRootContext };
function isItemExpanded(rootValue: string | string[] | undefined, value: string) {
return Array.isArray(rootValue) ? rootValue.includes(value) : rootValue === value;