import * as AccordionPrimitive from '@rn-primitives/accordion';
import * as React from 'react';
import { Platform, Pressable, View } from 'react-native';
} from 'react-native-reanimated';
import { ChevronDown } from '~/lib/icons/ChevronDown';
import { cn } from '~/lib/utils';
import { TextClassContext } from '~/components/ui/text';
const Accordion = React.forwardRef<AccordionPrimitive.RootRef, AccordionPrimitive.RootProps>(
({ children, ...props }, ref) => {
<LayoutAnimationConfig skipEntering>
<AccordionPrimitive.Root ref={ref} {...props} asChild={Platform.OS !== 'web'}>
<Animated.View layout={LinearTransition.duration(200)}>{children}</Animated.View>
</AccordionPrimitive.Root>
Accordion.displayName = AccordionPrimitive.Root.displayName;
const AccordionItem = React.forwardRef<AccordionPrimitive.ItemRef, AccordionPrimitive.ItemProps>(
({ className, value, ...props }, ref) => {
<Animated.View className={'overflow-hidden'} layout={LinearTransition.duration(200)}>
className={cn('border-b border-border', className)}
AccordionItem.displayName = AccordionPrimitive.Item.displayName;
const Trigger = Platform.OS === 'web' ? View : Pressable;
const AccordionTrigger = React.forwardRef<
AccordionPrimitive.TriggerRef,
AccordionPrimitive.TriggerProps
>(({ className, children, ...props }, ref) => {
const { isExpanded } = AccordionPrimitive.useItemContext();
const progress = useDerivedValue(() =>
isExpanded ? withTiming(1, { duration: 250 }) : withTiming(0, { duration: 200 })
const chevronStyle = useAnimatedStyle(() => ({
transform: [{ rotate: `${progress.value * 180}deg` }],
opacity: interpolate(progress.value, [0, 1], [1, 0.8], Extrapolation.CLAMP),
<TextClassContext.Provider value='native:text-lg font-medium web:group-hover:underline'>
<AccordionPrimitive.Header className='flex'>
<AccordionPrimitive.Trigger ref={ref} {...props} asChild>
'flex flex-row web:flex-1 items-center justify-between py-4 web:transition-all group web:focus-visible:outline-none web:focus-visible:ring-1 web:focus-visible:ring-muted-foreground',
<Animated.View style={chevronStyle}>
<ChevronDown size={18} className={'text-foreground shrink-0'} />
</AccordionPrimitive.Trigger>
</AccordionPrimitive.Header>
</TextClassContext.Provider>
AccordionTrigger.displayName = AccordionPrimitive.Trigger.displayName;
const AccordionContent = React.forwardRef<
AccordionPrimitive.ContentRef,
AccordionPrimitive.ContentProps
>(({ className, children, ...props }, ref) => {
const { isExpanded } = AccordionPrimitive.useItemContext();
<TextClassContext.Provider value='native:text-lg'>
<AccordionPrimitive.Content
'overflow-hidden text-sm web:transition-all',
isExpanded ? 'web:animate-accordion-down' : 'web:animate-accordion-up'
<InnerContent className={cn('pb-4', className)}>{children}</InnerContent>
</AccordionPrimitive.Content>
</TextClassContext.Provider>
function InnerContent({ children, className }: { children: React.ReactNode; className?: string }) {
if (Platform.OS === 'web') {
return <View className={cn('pb-4', className)}>{children}</View>;
exiting={FadeOutUp.duration(200)}
className={cn('pb-4', className)}
AccordionContent.displayName = AccordionPrimitive.Content.displayName;
export { Accordion, AccordionContent, AccordionItem, AccordionTrigger };