154 lines
4.4 KiB
TypeScript
154 lines
4.4 KiB
TypeScript
|
|
import React from 'react';
|
||
|
|
import { NavigationContainer } from '@react-navigation/native';
|
||
|
|
import { createBottomTabNavigator } from '@react-navigation/bottom-tabs';
|
||
|
|
import { createNativeStackNavigator } from '@react-navigation/native-stack';
|
||
|
|
import { View, Text, StyleSheet, TouchableOpacity } from 'react-native';
|
||
|
|
import { useSafeAreaInsets } from 'react-native-safe-area-context';
|
||
|
|
|
||
|
|
// lucide 아이콘 (모바일 웹과 동일)
|
||
|
|
import { Home, Users, Disc3, Calendar } from 'lucide-react-native';
|
||
|
|
|
||
|
|
// 스크린 import
|
||
|
|
import HomeScreen from '../screens/HomeScreen';
|
||
|
|
import MembersScreen from '../screens/MembersScreen';
|
||
|
|
import AlbumScreen from '../screens/AlbumScreen';
|
||
|
|
import AlbumDetailScreen from '../screens/AlbumDetailScreen';
|
||
|
|
import AlbumGalleryScreen from '../screens/AlbumGalleryScreen';
|
||
|
|
import ScheduleScreen from '../screens/ScheduleScreen';
|
||
|
|
|
||
|
|
import { colors } from '../constants/colors';
|
||
|
|
|
||
|
|
// 타입 정의
|
||
|
|
export type RootTabParamList = {
|
||
|
|
HomeTab: undefined;
|
||
|
|
MembersTab: undefined;
|
||
|
|
AlbumTab: undefined;
|
||
|
|
ScheduleTab: undefined;
|
||
|
|
};
|
||
|
|
|
||
|
|
export type AlbumStackParamList = {
|
||
|
|
AlbumList: undefined;
|
||
|
|
AlbumDetail: { name: string };
|
||
|
|
AlbumGallery: { name: string };
|
||
|
|
};
|
||
|
|
|
||
|
|
const Tab = createBottomTabNavigator<RootTabParamList>();
|
||
|
|
const AlbumStack = createNativeStackNavigator<AlbumStackParamList>();
|
||
|
|
|
||
|
|
// 앨범 스택 네비게이터
|
||
|
|
function AlbumStackNavigator() {
|
||
|
|
return (
|
||
|
|
<AlbumStack.Navigator
|
||
|
|
screenOptions={{
|
||
|
|
headerShown: false,
|
||
|
|
}}
|
||
|
|
>
|
||
|
|
<AlbumStack.Screen name="AlbumList" component={AlbumScreen} />
|
||
|
|
<AlbumStack.Screen name="AlbumDetail" component={AlbumDetailScreen} />
|
||
|
|
<AlbumStack.Screen name="AlbumGallery" component={AlbumGalleryScreen} />
|
||
|
|
</AlbumStack.Navigator>
|
||
|
|
);
|
||
|
|
}
|
||
|
|
|
||
|
|
// 커스텀 탭바 (모바일 웹과 동일한 스타일)
|
||
|
|
function CustomTabBar({ state, descriptors, navigation }: any) {
|
||
|
|
const insets = useSafeAreaInsets();
|
||
|
|
|
||
|
|
const tabItems = [
|
||
|
|
{ name: 'HomeTab', label: '홈', Icon: Home },
|
||
|
|
{ name: 'MembersTab', label: '멤버', Icon: Users },
|
||
|
|
{ name: 'AlbumTab', label: '앨범', Icon: Disc3 },
|
||
|
|
{ name: 'ScheduleTab', label: '일정', Icon: Calendar },
|
||
|
|
];
|
||
|
|
|
||
|
|
return (
|
||
|
|
<View style={[
|
||
|
|
styles.tabBar,
|
||
|
|
{ paddingBottom: Math.max(insets.bottom, 8) }
|
||
|
|
]}>
|
||
|
|
<View style={styles.tabBarContent}>
|
||
|
|
{state.routes.map((route: any, index: number) => {
|
||
|
|
const isFocused = state.index === index;
|
||
|
|
const item = tabItems[index];
|
||
|
|
const IconComponent = item.Icon;
|
||
|
|
|
||
|
|
const onPress = () => {
|
||
|
|
const event = navigation.emit({
|
||
|
|
type: 'tabPress',
|
||
|
|
target: route.key,
|
||
|
|
canPreventDefault: true,
|
||
|
|
});
|
||
|
|
if (!isFocused && !event.defaultPrevented) {
|
||
|
|
navigation.navigate(route.name);
|
||
|
|
}
|
||
|
|
};
|
||
|
|
|
||
|
|
return (
|
||
|
|
<TouchableOpacity
|
||
|
|
key={route.key}
|
||
|
|
onPress={onPress}
|
||
|
|
style={styles.tabItem}
|
||
|
|
activeOpacity={0.7}
|
||
|
|
>
|
||
|
|
<IconComponent
|
||
|
|
size={22}
|
||
|
|
strokeWidth={isFocused ? 2.5 : 2}
|
||
|
|
color={isFocused ? colors.primary : '#9CA3AF'}
|
||
|
|
/>
|
||
|
|
<Text style={[
|
||
|
|
styles.tabLabel,
|
||
|
|
{ color: isFocused ? colors.primary : '#9CA3AF' }
|
||
|
|
]}>
|
||
|
|
{item.label}
|
||
|
|
</Text>
|
||
|
|
</TouchableOpacity>
|
||
|
|
);
|
||
|
|
})}
|
||
|
|
</View>
|
||
|
|
</View>
|
||
|
|
);
|
||
|
|
}
|
||
|
|
|
||
|
|
// 메인 탭 네비게이터
|
||
|
|
export default function AppNavigator() {
|
||
|
|
return (
|
||
|
|
<NavigationContainer>
|
||
|
|
<Tab.Navigator
|
||
|
|
tabBar={(props) => <CustomTabBar {...props} />}
|
||
|
|
screenOptions={{
|
||
|
|
headerShown: false,
|
||
|
|
}}
|
||
|
|
>
|
||
|
|
<Tab.Screen name="HomeTab" component={HomeScreen} />
|
||
|
|
<Tab.Screen name="MembersTab" component={MembersScreen} />
|
||
|
|
<Tab.Screen name="AlbumTab" component={AlbumStackNavigator} />
|
||
|
|
<Tab.Screen name="ScheduleTab" component={ScheduleScreen} />
|
||
|
|
</Tab.Navigator>
|
||
|
|
</NavigationContainer>
|
||
|
|
);
|
||
|
|
}
|
||
|
|
|
||
|
|
const styles = StyleSheet.create({
|
||
|
|
tabBar: {
|
||
|
|
backgroundColor: '#FFFFFF',
|
||
|
|
borderTopWidth: 1,
|
||
|
|
borderTopColor: '#E5E7EB',
|
||
|
|
},
|
||
|
|
tabBarContent: {
|
||
|
|
flexDirection: 'row',
|
||
|
|
alignItems: 'center',
|
||
|
|
justifyContent: 'space-around',
|
||
|
|
height: 56,
|
||
|
|
},
|
||
|
|
tabItem: {
|
||
|
|
flex: 1,
|
||
|
|
alignItems: 'center',
|
||
|
|
justifyContent: 'center',
|
||
|
|
gap: 4,
|
||
|
|
},
|
||
|
|
tabLabel: {
|
||
|
|
fontSize: 11,
|
||
|
|
fontWeight: '500',
|
||
|
|
},
|
||
|
|
});
|