mirror of
https://github.com/DocNR/POWR.git
synced 2025-04-23 01:01:27 +00:00
126 lines
4.2 KiB
TypeScript
126 lines
4.2 KiB
TypeScript
![]() |
import * as AccordionPrimitive from '@rn-primitives/accordion';
|
||
|
import * as React from 'react';
|
||
|
import { Platform, Pressable, View } from 'react-native';
|
||
|
import Animated, {
|
||
|
Extrapolation,
|
||
|
FadeIn,
|
||
|
FadeOutUp,
|
||
|
LayoutAnimationConfig,
|
||
|
LinearTransition,
|
||
|
interpolate,
|
||
|
useAnimatedStyle,
|
||
|
useDerivedValue,
|
||
|
withTiming,
|
||
|
} 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) => {
|
||
|
return (
|
||
|
<LayoutAnimationConfig skipEntering>
|
||
|
<AccordionPrimitive.Root ref={ref} {...props} asChild={Platform.OS !== 'web'}>
|
||
|
<Animated.View layout={LinearTransition.duration(200)}>{children}</Animated.View>
|
||
|
</AccordionPrimitive.Root>
|
||
|
</LayoutAnimationConfig>
|
||
|
);
|
||
|
}
|
||
|
);
|
||
|
|
||
|
Accordion.displayName = AccordionPrimitive.Root.displayName;
|
||
|
|
||
|
const AccordionItem = React.forwardRef<AccordionPrimitive.ItemRef, AccordionPrimitive.ItemProps>(
|
||
|
({ className, value, ...props }, ref) => {
|
||
|
return (
|
||
|
<Animated.View className={'overflow-hidden'} layout={LinearTransition.duration(200)}>
|
||
|
<AccordionPrimitive.Item
|
||
|
ref={ref}
|
||
|
className={cn('border-b border-border', className)}
|
||
|
value={value}
|
||
|
{...props}
|
||
|
/>
|
||
|
</Animated.View>
|
||
|
);
|
||
|
}
|
||
|
);
|
||
|
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),
|
||
|
}));
|
||
|
|
||
|
return (
|
||
|
<TextClassContext.Provider value='native:text-lg font-medium web:group-hover:underline'>
|
||
|
<AccordionPrimitive.Header className='flex'>
|
||
|
<AccordionPrimitive.Trigger ref={ref} {...props} asChild>
|
||
|
<Trigger
|
||
|
className={cn(
|
||
|
'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',
|
||
|
className
|
||
|
)}
|
||
|
>
|
||
|
<>{children}</>
|
||
|
<Animated.View style={chevronStyle}>
|
||
|
<ChevronDown size={18} className={'text-foreground shrink-0'} />
|
||
|
</Animated.View>
|
||
|
</Trigger>
|
||
|
</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();
|
||
|
return (
|
||
|
<TextClassContext.Provider value='native:text-lg'>
|
||
|
<AccordionPrimitive.Content
|
||
|
className={cn(
|
||
|
'overflow-hidden text-sm web:transition-all',
|
||
|
isExpanded ? 'web:animate-accordion-down' : 'web:animate-accordion-up'
|
||
|
)}
|
||
|
ref={ref}
|
||
|
{...props}
|
||
|
>
|
||
|
<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>;
|
||
|
}
|
||
|
return (
|
||
|
<Animated.View
|
||
|
entering={FadeIn}
|
||
|
exiting={FadeOutUp.duration(200)}
|
||
|
className={cn('pb-4', className)}
|
||
|
>
|
||
|
{children}
|
||
|
</Animated.View>
|
||
|
);
|
||
|
}
|
||
|
|
||
|
AccordionContent.displayName = AccordionPrimitive.Content.displayName;
|
||
|
|
||
|
export { Accordion, AccordionContent, AccordionItem, AccordionTrigger };
|