tab-bar.tsx 3.7 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143
  1. import React from 'react'
  2. import { View, Text } from '@tarojs/components'
  3. import { useRouter } from '@tarojs/taro'
  4. import clsx from 'clsx'
  5. export interface TabBarItem {
  6. key: string
  7. title: string
  8. icon?: string
  9. selectedIcon?: string
  10. badge?: number | string
  11. dot?: boolean
  12. }
  13. export interface TabBarProps {
  14. items: TabBarItem[]
  15. activeKey?: string
  16. onChange?: (key: string) => void
  17. className?: string
  18. style?: React.CSSProperties
  19. fixed?: boolean
  20. safeArea?: boolean
  21. color?: string
  22. selectedColor?: string
  23. backgroundColor?: string
  24. }
  25. const TabBar = React.forwardRef<HTMLDivElement, TabBarProps>(({
  26. items,
  27. activeKey,
  28. onChange,
  29. className,
  30. style,
  31. fixed = true,
  32. safeArea = true,
  33. color = '#7f7f7f',
  34. selectedColor = '#1890ff',
  35. backgroundColor = '#ffffff',
  36. }, ref) => {
  37. const router = useRouter()
  38. const currentActiveKey = activeKey || items[0]?.key
  39. const handleTabChange = (key: string) => {
  40. if (key !== currentActiveKey) {
  41. onChange?.(key)
  42. }
  43. }
  44. return (
  45. <View
  46. ref={ref}
  47. className={clsx(
  48. 'tab-bar',
  49. fixed && 'fixed bottom-0 left-0 right-0',
  50. safeArea && 'pb-safe',
  51. 'z-50',
  52. className
  53. )}
  54. style={{
  55. backgroundColor,
  56. ...style,
  57. }}
  58. >
  59. <View className="flex h-16 border-t border-gray-200">
  60. {items.map((item) => {
  61. const isActive = item.key === currentActiveKey
  62. return (
  63. <View
  64. key={item.key}
  65. className={clsx(
  66. 'flex-1 flex flex-col items-center justify-center',
  67. 'px-2 py-1',
  68. 'cursor-pointer',
  69. 'transition-colors duration-200',
  70. 'hover:opacity-80'
  71. )}
  72. onClick={() => handleTabChange(item.key)}
  73. >
  74. <View className="relative">
  75. {item.icon && (
  76. <View
  77. className={clsx(
  78. 'text-2xl',
  79. 'mb-1',
  80. isActive ? 'text-blue-500' : 'text-gray-500'
  81. )}
  82. style={{
  83. color: isActive ? selectedColor : color,
  84. }}
  85. >
  86. {isActive && item.selectedIcon ? item.selectedIcon : item.icon}
  87. </View>
  88. )}
  89. {item.badge && (
  90. <View
  91. className={clsx(
  92. 'absolute -top-1 -right-2',
  93. 'bg-red-500 text-white text-xs',
  94. 'rounded-full px-1.5 py-0.5',
  95. 'min-w-4 h-4 flex items-center justify-center'
  96. )}
  97. >
  98. {typeof item.badge === 'number' && item.badge > 99 ? '99+' : item.badge}
  99. </View>
  100. )}
  101. {item.dot && (
  102. <View
  103. className={clsx(
  104. 'absolute -top-1 -right-1',
  105. 'w-2 h-2 bg-red-500 rounded-full'
  106. )}
  107. />
  108. )}
  109. </View>
  110. <Text
  111. className={clsx(
  112. 'text-xs',
  113. 'leading-tight',
  114. isActive ? 'font-medium' : 'font-normal'
  115. )}
  116. style={{
  117. color: isActive ? selectedColor : color,
  118. }}
  119. numberOfLines={1}
  120. >
  121. {item.title}
  122. </Text>
  123. </View>
  124. )
  125. })}
  126. </View>
  127. </View>
  128. )
  129. })
  130. TabBar.displayName = 'TabBar'
  131. export { TabBar }