Files
rn-app/pages/HomeScreen/components/GameSubMenus/index.tsx
2025-11-13 16:47:10 +08:00

232 lines
6.7 KiB
TypeScript

/**
* 游戏子菜单组件
* 基于 xinyong-web 的 SubGameCategoryMenu 组件重建
* 功能包括:
* - 水平/竖直菜单切换
* - 菜单项滚动和自动定位
* - 弹窗选择菜单
* - 主题适配
*/
import React, { useState, useCallback, useMemo, useRef, useEffect } from 'react';
import {
View,
Text,
ScrollView,
TouchableOpacity,
Modal,
FlatList,
Dimensions,
} from 'react-native';
import { Ionicons } from '@expo/vector-icons';
import { useColorScheme, useThemeColors } from '@/theme';
import { styles } from './styles';
import { useGameMainMenus, useGameMenuTabs } from '@/hooks/useGameMenus';
import { Image } from '@/components';
const { width } = Dimensions.get('window');
interface GameSubMenusProps {
vertical?: boolean;
onSubMenuChange?: (menuKey: string) => void;
}
/**
* 游戏子菜单组件
*/
export default function GameSubMenus({ vertical = false, onSubMenuChange }: GameSubMenusProps) {
const colorScheme = useColorScheme();
const s = styles[colorScheme];
const themeColors = useThemeColors();
// 获取主菜单数据
const gameMenus = useGameMainMenus(colorScheme);
const { activeMainMenuTab, activeSubMenuTab, setActiveSubMenuTab } = useGameMenuTabs();
// 获取当前选中的主菜单
const currentMenu = useMemo(() => {
return gameMenus.find((menu) => menu.key === activeMainMenuTab);
}, [gameMenus, activeMainMenuTab]);
// 获取当前选中的子菜单
const subMenus = useMemo((): Record<string, any>[] => {
return currentMenu?.children || [];
}, [currentMenu]);
// 弹窗状态
const [showPopup, setShowPopup] = useState(false);
// 滚动视图引用
const scrollViewRef = useRef<ScrollView>(null);
// 当前选中的子菜单索引
const selectedSubMenuIndex = useMemo(() => {
return subMenus.findIndex((menu) => menu.key === activeSubMenuTab);
}, [subMenus, activeSubMenuTab]);
// 处理子菜单选择
const handleSubMenuPress = useCallback(
(menuKey: string) => {
setActiveSubMenuTab(menuKey);
onSubMenuChange?.(menuKey);
},
[setActiveSubMenuTab, onSubMenuChange]
);
// 处理打开弹窗
const handleOpenPopup = useCallback(() => {
setShowPopup(true);
}, []);
// 处理关闭弹窗
const handleClosePopup = useCallback(() => {
setShowPopup(false);
}, []);
// 处理弹窗中的菜单选择
const handlePopupMenuSelect = useCallback(
(menuKey: string) => {
handleSubMenuPress(menuKey);
handleClosePopup();
},
[handleSubMenuPress, handleClosePopup]
);
// 自动滚动到选中项
useEffect(() => {
if (selectedSubMenuIndex >= 0 && !vertical) {
const scrollPosition = selectedSubMenuIndex * 112; // 100 (width) + 12 (margin)
scrollViewRef.current?.scrollTo({
x: scrollPosition,
animated: true,
});
}
}, [selectedSubMenuIndex, vertical]);
// 如果没有子菜单,返回空
if (subMenus.length === 0) {
return null;
}
// 渲染菜单项
const renderMenuItem = (item: any, isActive: boolean) => (
<TouchableOpacity
style={[s.menuItem, vertical && s.menuItemVertical, isActive && s.menuItemActive]}
onPress={() => handleSubMenuPress(item.key)}
activeOpacity={0.7}
>
{item.colorImgSrc && (
<Image
source={item.colorImgSrc}
style={[s.menuIcon]}
adaptiveMode="height"
autoMeasure={true}
/>
)}
<Text style={[s.menuText, isActive && s.menuTextActive]} numberOfLines={2}>
{item.name}
</Text>
</TouchableOpacity>
);
// 水平布局
if (!vertical) {
return (
<View style={s.horizontalContainer}>
<ScrollView
ref={scrollViewRef}
horizontal
showsHorizontalScrollIndicator={false}
scrollEventThrottle={16}
style={s.horizontalScrollView}
>
{subMenus.map((item) => (
<View key={item.key}>{renderMenuItem(item, item.key === activeSubMenuTab)}</View>
))}
{/* 更多菜单按钮 */}
<TouchableOpacity
style={[s.menuItem, { marginLeft: 6 }]}
onPress={handleOpenPopup}
activeOpacity={0.7}
>
<Ionicons name="list" size={32} color={themeColors.text + '80'} />
<Text style={s.menuText}></Text>
</TouchableOpacity>
</ScrollView>
</View>
);
}
// 竖直布局
return (
<View style={s.verticalContainer}>
<ScrollView
ref={scrollViewRef}
showsVerticalScrollIndicator={false}
scrollEventThrottle={16}
style={s.verticalScrollView}
>
{subMenus.map((item) => (
<View key={item.key}>{renderMenuItem(item, item.key === activeSubMenuTab)}</View>
))}
</ScrollView>
{/* 弹窗 */}
<Modal
visible={showPopup}
transparent
animationType="slide"
onRequestClose={handleClosePopup}
>
<View style={s.modalOverlay}>
<TouchableOpacity style={{ flex: 1 }} activeOpacity={1} onPress={handleClosePopup} />
<View style={s.modalContent}>
{/* 弹窗头部 */}
<View style={s.modalHeader}>
<Text style={s.modalTitle}></Text>
<TouchableOpacity style={s.modalCloseButton} onPress={handleClosePopup}>
<Ionicons name="close" size={24} color={themeColors.text} />
</TouchableOpacity>
</View>
{/* 弹窗内容 - 网格布局 */}
<FlatList
data={subMenus}
renderItem={({ item }) => (
<TouchableOpacity
style={[s.modalGridItem, item.key === activeSubMenuTab && s.modalGridItemActive]}
onPress={() => handlePopupMenuSelect(item.key)}
activeOpacity={0.7}
>
{item.colorImgSrc && (
<Image
source={item.colorImgSrc}
style={s.modalGridIcon}
resizeMode="contain"
adaptiveMode="height"
autoMeasure={true}
/>
)}
<Text
style={[
s.modalGridText,
item.key === activeSubMenuTab && s.modalGridTextActive,
]}
numberOfLines={2}
>
{item.name}
</Text>
</TouchableOpacity>
)}
keyExtractor={(item) => item.key}
numColumns={2}
scrollEnabled={false}
contentContainerStyle={s.modalGrid}
/>
</View>
</View>
</Modal>
</View>
);
}