- packages/ui-native theme.ts accent: amber->main, amberDim->dim, amberGlow->glow (값 보존)
- apps/mobile 20건 + apps/mobile-rn 18건 + ui-native 컴포넌트 9건 = 47 참조 치환
SKIP: Led/PhosphorText/Header color prop 'amber'(의미론적 컴포넌트 API, 별도 마이그레이션)
green/greenGlow 키(amber계 범위外)
정책: docs/REFACTOR_POLICY.md DP1, 이식 인사이트 P7
149 lines
4.5 KiB
TypeScript
149 lines
4.5 KiB
TypeScript
// src/navigation/TabNavigator.tsx — Bottom tabs: DASH / HIST / REC / TALK / SET
|
|
import { View, StyleSheet, Pressable, Platform } from 'react-native'
|
|
import { createBottomTabNavigator } from '@react-navigation/bottom-tabs'
|
|
import { d3roNativePalette } from '@d3ro/ui-native'
|
|
import DashScreen from '../screens/DashScreen'
|
|
import HistoryScreen from '../screens/HistoryScreen'
|
|
import RecordScreen from '../screens/RecordScreen'
|
|
import TalkScreen from '../screens/TalkScreen'
|
|
import SettingsScreen from '../screens/SettingsScreen'
|
|
|
|
const MONO = Platform.OS === 'ios' ? 'Menlo' : 'monospace'
|
|
const Tab = createBottomTabNavigator()
|
|
|
|
function TabIcon({ type, color }: { type: string; color: string }): React.ReactElement {
|
|
if (type === 'dash') {
|
|
return (
|
|
<View style={[iconStyles.grid, { borderColor: color }]}>
|
|
{[0, 1, 2, 3].map((i) => (
|
|
<View key={i} style={[iconStyles.gridCell, { borderColor: color }]} />
|
|
))}
|
|
</View>
|
|
)
|
|
}
|
|
if (type === 'hist') {
|
|
return (
|
|
<View style={[iconStyles.circle, { borderColor: color }]}>
|
|
<View style={[iconStyles.hand, { backgroundColor: color }]} />
|
|
</View>
|
|
)
|
|
}
|
|
if (type === 'talk') {
|
|
return <View style={[iconStyles.chat, { borderColor: color }]} />
|
|
}
|
|
if (type === 'set') {
|
|
return <View style={[iconStyles.gear, { borderColor: color }]} />
|
|
}
|
|
return <View />
|
|
}
|
|
|
|
function RecordFAB(): React.ReactElement {
|
|
return (
|
|
<View style={fabStyles.fab}>
|
|
<View style={fabStyles.micIcon}>
|
|
<View style={fabStyles.micBody} />
|
|
<View style={fabStyles.micBase} />
|
|
</View>
|
|
</View>
|
|
)
|
|
}
|
|
|
|
export default function TabNavigator(): React.ReactElement {
|
|
return (
|
|
<Tab.Navigator
|
|
screenOptions={{
|
|
tabBarStyle: {
|
|
backgroundColor: 'rgba(25, 25, 27, 0.95)',
|
|
borderTopColor: d3roNativePalette.border.default,
|
|
borderTopWidth: 1,
|
|
height: 80,
|
|
paddingBottom: 20,
|
|
paddingTop: 8,
|
|
},
|
|
tabBarActiveTintColor: d3roNativePalette.accent.main,
|
|
tabBarInactiveTintColor: d3roNativePalette.text.muted,
|
|
tabBarLabelStyle: {
|
|
fontFamily: MONO,
|
|
fontSize: 9,
|
|
fontWeight: '500',
|
|
letterSpacing: 0.5,
|
|
},
|
|
headerShown: false,
|
|
}}
|
|
>
|
|
<Tab.Screen
|
|
name="Dash"
|
|
component={DashScreen}
|
|
options={{
|
|
title: 'DASH',
|
|
tabBarIcon: ({ color }) => <TabIcon type="dash" color={color} />,
|
|
}}
|
|
/>
|
|
<Tab.Screen
|
|
name="History"
|
|
component={HistoryScreen}
|
|
options={{
|
|
title: 'HIST',
|
|
tabBarIcon: ({ color }) => <TabIcon type="hist" color={color} />,
|
|
}}
|
|
/>
|
|
<Tab.Screen
|
|
name="Record"
|
|
component={RecordScreen}
|
|
options={{
|
|
title: '',
|
|
tabBarIcon: () => <RecordFAB />,
|
|
tabBarLabel: () => null,
|
|
}}
|
|
/>
|
|
<Tab.Screen
|
|
name="Talk"
|
|
component={TalkScreen}
|
|
options={{
|
|
title: 'TALK',
|
|
tabBarIcon: ({ color }) => <TabIcon type="talk" color={color} />,
|
|
}}
|
|
/>
|
|
<Tab.Screen
|
|
name="Settings"
|
|
component={SettingsScreen}
|
|
options={{
|
|
title: 'SET',
|
|
tabBarIcon: ({ color }) => <TabIcon type="set" color={color} />,
|
|
}}
|
|
/>
|
|
</Tab.Navigator>
|
|
)
|
|
}
|
|
|
|
const iconStyles = StyleSheet.create({
|
|
grid: { width: 22, height: 22, flexDirection: 'row', flexWrap: 'wrap', gap: 2 },
|
|
gridCell: { width: 9, height: 9, borderWidth: 1.5, borderRadius: 2 },
|
|
circle: { width: 22, height: 22, borderRadius: 11, borderWidth: 1.5, justifyContent: 'center', alignItems: 'center' },
|
|
hand: { width: 1.5, height: 7, position: 'absolute', top: 3 },
|
|
chat: { width: 22, height: 18, borderWidth: 1.5, borderRadius: 4 },
|
|
gear: { width: 22, height: 22, borderWidth: 1.5, borderRadius: 11 },
|
|
})
|
|
|
|
const fabStyles = StyleSheet.create({
|
|
fab: {
|
|
position: 'relative',
|
|
top: -20,
|
|
width: 56,
|
|
height: 56,
|
|
borderRadius: 28,
|
|
backgroundColor: d3roNativePalette.accent.main,
|
|
justifyContent: 'center',
|
|
alignItems: 'center',
|
|
shadowColor: d3roNativePalette.accent.main,
|
|
shadowOffset: { width: 0, height: 0 },
|
|
shadowOpacity: 0.4,
|
|
shadowRadius: 10,
|
|
elevation: 8,
|
|
borderWidth: 4,
|
|
borderColor: d3roNativePalette.bg.app,
|
|
},
|
|
micIcon: { alignItems: 'center' },
|
|
micBody: { width: 8, height: 14, borderRadius: 4, backgroundColor: d3roNativePalette.bg.app, marginBottom: 2 },
|
|
micBase: { width: 14, height: 2, borderRadius: 1, backgroundColor: d3roNativePalette.bg.app },
|
|
})
|