157 lines
4.2 KiB
TypeScript
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>
|
|
)
|
|
}
|