|
|
@@ -0,0 +1,143 @@
|
|
|
+import React from 'react'
|
|
|
+import { View, Text } from '@tarojs/components'
|
|
|
+import { useRouter } from '@tarojs/taro'
|
|
|
+import clsx from 'clsx'
|
|
|
+
|
|
|
+export interface TabBarItem {
|
|
|
+ key: string
|
|
|
+ title: string
|
|
|
+ icon?: string
|
|
|
+ selectedIcon?: string
|
|
|
+ badge?: number | string
|
|
|
+ dot?: boolean
|
|
|
+}
|
|
|
+
|
|
|
+export interface TabBarProps {
|
|
|
+ items: TabBarItem[]
|
|
|
+ activeKey?: string
|
|
|
+ onChange?: (key: string) => void
|
|
|
+ className?: string
|
|
|
+ style?: React.CSSProperties
|
|
|
+ fixed?: boolean
|
|
|
+ safeArea?: boolean
|
|
|
+ color?: string
|
|
|
+ selectedColor?: string
|
|
|
+ backgroundColor?: string
|
|
|
+}
|
|
|
+
|
|
|
+const TabBar = React.forwardRef<HTMLDivElement, TabBarProps>(({
|
|
|
+ items,
|
|
|
+ activeKey,
|
|
|
+ onChange,
|
|
|
+ className,
|
|
|
+ style,
|
|
|
+ fixed = true,
|
|
|
+ safeArea = true,
|
|
|
+ color = '#7f7f7f',
|
|
|
+ selectedColor = '#1890ff',
|
|
|
+ backgroundColor = '#ffffff',
|
|
|
+}, ref) => {
|
|
|
+ const router = useRouter()
|
|
|
+
|
|
|
+ const currentActiveKey = activeKey || items[0]?.key
|
|
|
+
|
|
|
+ const handleTabChange = (key: string) => {
|
|
|
+ if (key !== currentActiveKey) {
|
|
|
+ onChange?.(key)
|
|
|
+ }
|
|
|
+ }
|
|
|
+
|
|
|
+ return (
|
|
|
+ <View
|
|
|
+ ref={ref}
|
|
|
+ className={clsx(
|
|
|
+ 'tab-bar',
|
|
|
+ fixed && 'fixed bottom-0 left-0 right-0',
|
|
|
+ safeArea && 'pb-safe',
|
|
|
+ 'z-50',
|
|
|
+ className
|
|
|
+ )}
|
|
|
+ style={{
|
|
|
+ backgroundColor,
|
|
|
+ ...style,
|
|
|
+ }}
|
|
|
+ >
|
|
|
+ <View className="flex h-16 border-t border-gray-200">
|
|
|
+ {items.map((item) => {
|
|
|
+ const isActive = item.key === currentActiveKey
|
|
|
+
|
|
|
+ return (
|
|
|
+ <View
|
|
|
+ key={item.key}
|
|
|
+ className={clsx(
|
|
|
+ 'flex-1 flex flex-col items-center justify-center',
|
|
|
+ 'px-2 py-1',
|
|
|
+ 'cursor-pointer',
|
|
|
+ 'transition-colors duration-200',
|
|
|
+ 'hover:opacity-80'
|
|
|
+ )}
|
|
|
+ onClick={() => handleTabChange(item.key)}
|
|
|
+ >
|
|
|
+ <View className="relative">
|
|
|
+ {item.icon && (
|
|
|
+ <View
|
|
|
+ className={clsx(
|
|
|
+ 'text-2xl',
|
|
|
+ 'mb-1',
|
|
|
+ isActive ? 'text-blue-500' : 'text-gray-500'
|
|
|
+ )}
|
|
|
+ style={{
|
|
|
+ color: isActive ? selectedColor : color,
|
|
|
+ }}
|
|
|
+ >
|
|
|
+ {isActive && item.selectedIcon ? item.selectedIcon : item.icon}
|
|
|
+ </View>
|
|
|
+ )}
|
|
|
+
|
|
|
+ {item.badge && (
|
|
|
+ <View
|
|
|
+ className={clsx(
|
|
|
+ 'absolute -top-1 -right-2',
|
|
|
+ 'bg-red-500 text-white text-xs',
|
|
|
+ 'rounded-full px-1.5 py-0.5',
|
|
|
+ 'min-w-4 h-4 flex items-center justify-center'
|
|
|
+ )}
|
|
|
+ >
|
|
|
+ {typeof item.badge === 'number' && item.badge > 99 ? '99+' : item.badge}
|
|
|
+ </View>
|
|
|
+ )}
|
|
|
+
|
|
|
+ {item.dot && (
|
|
|
+ <View
|
|
|
+ className={clsx(
|
|
|
+ 'absolute -top-1 -right-1',
|
|
|
+ 'w-2 h-2 bg-red-500 rounded-full'
|
|
|
+ )}
|
|
|
+ />
|
|
|
+ )}
|
|
|
+ </View>
|
|
|
+
|
|
|
+ <Text
|
|
|
+ className={clsx(
|
|
|
+ 'text-xs',
|
|
|
+ 'leading-tight',
|
|
|
+ isActive ? 'font-medium' : 'font-normal'
|
|
|
+ )}
|
|
|
+ style={{
|
|
|
+ color: isActive ? selectedColor : color,
|
|
|
+ }}
|
|
|
+ numberOfLines={1}
|
|
|
+ >
|
|
|
+ {item.title}
|
|
|
+ </Text>
|
|
|
+ </View>
|
|
|
+ )
|
|
|
+ })}
|
|
|
+ </View>
|
|
|
+ </View>
|
|
|
+ )
|
|
|
+})
|
|
|
+
|
|
|
+TabBar.displayName = 'TabBar'
|
|
|
+
|
|
|
+export { TabBar }
|