78 lines
1.7 KiB
TypeScript
78 lines
1.7 KiB
TypeScript
import React, { memo, useCallback, useMemo } from 'react'
|
|
import { Pressable, StyleSheet, Text, View } from 'react-native'
|
|
import { Ionicons } from '@expo/vector-icons'
|
|
|
|
export interface FavoriteButtonProps {
|
|
favorited?: boolean
|
|
loading?: boolean
|
|
count?: number
|
|
size?: number
|
|
onPress?: () => void
|
|
testID?: string
|
|
}
|
|
|
|
const FavoriteButtonComponent: React.FC<FavoriteButtonProps> = ({
|
|
favorited = false,
|
|
loading = false,
|
|
count,
|
|
size = 24,
|
|
onPress,
|
|
testID,
|
|
}) => {
|
|
// 使用 useCallback 缓存 onPress 回调
|
|
const handlePress = useCallback(() => {
|
|
if (!loading && onPress) {
|
|
onPress()
|
|
}
|
|
}, [loading, onPress])
|
|
|
|
// 缓存样式计算 - 移除不必要的 iconStyle
|
|
const containerStyle = useMemo(
|
|
() => [styles.container, loading && styles.disabled],
|
|
[loading]
|
|
)
|
|
|
|
// 根据收藏状态选择图标名称和颜色
|
|
const iconName = favorited ? 'star' : 'star-outline'
|
|
const iconColor = favorited ? '#FFD700' : '#8E8E93'
|
|
|
|
return (
|
|
<Pressable
|
|
onPress={handlePress}
|
|
disabled={loading}
|
|
testID={testID}
|
|
style={containerStyle}
|
|
>
|
|
<View style={styles.content}>
|
|
<Ionicons name={iconName} size={size} color={iconColor} />
|
|
{count !== undefined && (
|
|
<Text style={[styles.count, { fontSize: size * 0.7 }]}>{count}</Text>
|
|
)}
|
|
</View>
|
|
</Pressable>
|
|
)
|
|
}
|
|
|
|
export const FavoriteButton = memo(FavoriteButtonComponent)
|
|
|
|
const styles = StyleSheet.create({
|
|
container: {
|
|
flexDirection: 'row',
|
|
alignItems: 'center',
|
|
padding: 8,
|
|
},
|
|
disabled: {
|
|
opacity: 0.5,
|
|
},
|
|
content: {
|
|
flexDirection: 'row',
|
|
alignItems: 'center',
|
|
gap: 4,
|
|
},
|
|
count: {
|
|
color: '#8E8E93',
|
|
fontWeight: '600',
|
|
marginLeft: 4,
|
|
},
|
|
})
|