tab-bar.tsx 4.2 KB

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