// packages/ui-native/src/components/WaveBars.tsx // RN WaveBars — animated audio level bars using core Animated API import { useEffect, useRef } from 'react' import { View, Animated, Easing, type ViewStyle, type StyleProp } from 'react-native' import { d3roNativePalette } from '../theme' export interface WaveBarsProps { active?: boolean barCount?: number style?: StyleProp } const BAR_MIN = 8 const BAR_MAX = 32 const DURATIONS = [800, 1000, 900, 1200, 1100, 950, 1050, 1300, 850, 1150, 1000] function WaveBar({ index, active }: { index: number; active: boolean }): React.ReactElement { const height = useRef(new Animated.Value(BAR_MIN)).current useEffect(() => { if (active) { const duration = DURATIONS[index % DURATIONS.length] const animation = Animated.loop( Animated.sequence([ Animated.timing(height, { toValue: BAR_MAX, duration, easing: Easing.inOut(Easing.ease), useNativeDriver: false, delay: index * 60 }), Animated.timing(height, { toValue: BAR_MIN, duration, easing: Easing.inOut(Easing.ease), useNativeDriver: false }) ]) ) animation.start() return () => animation.stop() } else { Animated.timing(height, { toValue: BAR_MIN, duration: 300, useNativeDriver: false }).start() } }, [active, height, index]) return ( ) } export function WaveBars({ active = false, barCount = 11, style }: WaveBarsProps): React.ReactElement { const containerStyle: ViewStyle = { height: 128, backgroundColor: d3roNativePalette.bg.inset, flexDirection: 'row', alignItems: 'center', justifyContent: 'center', gap: 6, borderBottomWidth: 1, borderBottomColor: d3roNativePalette.border.subtle } return ( {Array.from({ length: barCount }).map((_, i) => ( ))} ) }