DS 컴포넌트 9개 (ScreenPanel, WaveBars, AppStatusBar, Header, FilterChip 신규), 5개 탭 + 로그인 화면 D3RO 인스트루먼트 미학 적용, i18n 연결 (I18nProvider + 모바일 전용 키 80+개), 모노레포 workspaces에 apps/mobile 추가.
87 lines
2.2 KiB
TypeScript
87 lines
2.2 KiB
TypeScript
// 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<ViewStyle>
|
|
}
|
|
|
|
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 (
|
|
<Animated.View
|
|
style={{
|
|
width: 6,
|
|
height,
|
|
backgroundColor: d3roNativePalette.accent.amber,
|
|
borderRadius: 3
|
|
}}
|
|
/>
|
|
)
|
|
}
|
|
|
|
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 (
|
|
<View style={[containerStyle, style]}>
|
|
{Array.from({ length: barCount }).map((_, i) => (
|
|
<WaveBar key={i} index={i} active={active} />
|
|
))}
|
|
</View>
|
|
)
|
|
}
|