Files
workspace/code/allapp/allapp-rn/src/app/(tabs)/_layout.tsx
T
2026-03-03 22:05:25 +08:00

157 lines
4.2 KiB
TypeScript

import type { SortableGridRenderItem } from 'react-native-sortables'
import type { NavItem } from '@/store/navigation'
import { Slot, usePathname, useRouter } from 'expo-router'
import { useCallback, useEffect, useState } from 'react'
import { Text, TouchableOpacity, View } from 'react-native'
import { KeyboardAvoidingView } from 'react-native-keyboard-controller'
import Animated, { useAnimatedRef } from 'react-native-reanimated'
import Sortable from 'react-native-sortables'
import { ADrawer, LucideIcon } from '@/components/common/ao'
import { Divider } from '@/components/ui/divider'
import { useNavStore } from '@/store/navigation'
export default function TabsLayout() {
const scrollableRef = useAnimatedRef<Animated.ScrollView>()
const router = useRouter()
const pathname = usePathname()
const navStore = useNavStore()
const [showNavDrawer, setShowNavDrawer] = useState(false)
const [currentNav, setCurrentNav] = useState<NavItem>({
label: 'Welcome',
path: '/',
icon: 'Sparkles',
})
useEffect(() => {
const current = navStore.navMenus.find(item => item.path === pathname)
if (current) {
setCurrentNav(current)
}
else {
setCurrentNav({
label: 'Welcome',
path: '/',
icon: 'Sparkles',
})
}
}, [pathname])
const refreshPage = () => {
router.replace(pathname)
}
const renderNavItem = useCallback<SortableGridRenderItem<NavItem>>(
({ item }) => (
<TouchableOpacity
className="
flex
flex-col
items-center
justify-center
bg-[#f9f9f9]
rounded-md
p-3
"
onPress={() => {
setShowNavDrawer(false)
router.navigate(item.path)
}}
>
<LucideIcon icon={item.icon} size={24} strokeWidth={2} clickable={false} />
<Text className="text-sm mt-2">
{item.label}
</Text>
</TouchableOpacity>
),
[],
)
return (
<View className="flex-1 bg-gray-100">
<View className="flex-1 bg-white">
<KeyboardAvoidingView
behavior="padding"
style={{
flex: 1,
}}
>
<Slot />
</KeyboardAvoidingView>
</View>
<View
className="
h-14
flex-row items-center
px-2
gap-4
bg-white
"
style={{
zIndex: 0,
elevation: 24,
shadowColor: '#000',
shadowOffset: { width: 0, height: 20 },
borderTopWidth: 1,
borderTopColor: '#f2f3f5',
}}
>
<TouchableOpacity className="w-12 items-center justify-center">
<LucideIcon icon="Earth" size={24} clickable={false} strokeWidth={2} />
</TouchableOpacity>
<Divider orientation="vertical" className="h-8" />
<TouchableOpacity
className="flex-1 items-center justify-center flex flex-row"
onPress={() => {
setShowNavDrawer(true)
}}
>
<LucideIcon icon={currentNav.icon} size={22} strokeWidth={2} />
<Text className="font-semibold text-lg ml-2">{currentNav.label}</Text>
</TouchableOpacity>
<Divider orientation="vertical" className="h-8" />
<TouchableOpacity className="w-12 items-center justify-center" onPress={refreshPage}>
<LucideIcon icon="RefreshCcw" size={24} clickable={false} strokeWidth={2} />
</TouchableOpacity>
</View>
<ADrawer
visible={showNavDrawer}
height="60%"
title="菜单导航"
isCircle
hideModal={() => {
setShowNavDrawer(false)
}}
>
<Animated.ScrollView
ref={scrollableRef}
className="px-4 py-2 flex-1"
>
<Sortable.Grid
columnGap={16}
rowGap={16}
columns={3}
data={navStore.navMenus}
keyExtractor={item => item.path}
renderItem={renderNavItem}
scrollableRef={scrollableRef}
autoScrollActivationOffset={200}
showDropIndicator
onDragEnd={(dragData) => {
navStore.setNavMenus(dragData.data)
}}
/>
</Animated.ScrollView>
</ADrawer>
</View>
)
}