Files
expo-popcore-app/components/blocks/home/TemplateCard.tsx
imeepos a529dc03e1 fix: update @repo/sdk to version 1.0.14 and adjust build scripts for production
feat: modify TemplateCard and TemplateGrid to handle optional id and filter templates
refactor: clean up use-template-filter to use CategoryTemplate type
2026-01-26 16:40:32 +08:00

130 lines
2.9 KiB
TypeScript
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
import React, { memo } from 'react'
import { Pressable, StyleSheet, Text, View, ViewStyle } from 'react-native'
import { Image } from 'expo-image'
import { LinearGradient } from 'expo-linear-gradient'
export interface TemplateCardProps {
id?: string
title: string
previewUrl?: string
webpPreviewUrl?: string
coverImageUrl?: string
aspectRatio?: string
cardWidth: number
onPress: (id: string) => void
testID?: string
}
/**
* 解析 aspectRatio 字符串为数字
* 例如: "128:128" -> 1, "16:9" -> 1.777..., "1.5" -> 1.5
*/
export function parseAspectRatio(aspectRatioString?: string): number | undefined {
if (!aspectRatioString) return undefined
if (aspectRatioString.includes(':')) {
const [w, h] = aspectRatioString.split(':').map(Number)
return w / h
}
return Number(aspectRatioString)
}
/**
* 获取图片源 URI按优先级: webpPreviewUrl > previewUrl > coverImageUrl
*/
export function getImageUri(
webpPreviewUrl?: string,
previewUrl?: string,
coverImageUrl?: string
): string | undefined {
return webpPreviewUrl || previewUrl || coverImageUrl
}
const TemplateCardComponent: React.FC<TemplateCardProps> = ({
id,
title,
previewUrl,
webpPreviewUrl,
coverImageUrl,
aspectRatio: aspectRatioString,
cardWidth,
onPress,
testID,
}) => {
const aspectRatio = parseAspectRatio(aspectRatioString)
const imageUri = getImageUri(webpPreviewUrl, previewUrl, coverImageUrl)
// 如果没有 id则不渲染卡片
if (!id) {
return null
}
return (
<Pressable
style={[styles.card, { width: cardWidth }]}
onPress={() => onPress(id)}
testID={testID}
>
<View
style={[
styles.cardImageContainer,
aspectRatio ? { aspectRatio } : undefined,
].filter(Boolean) as ViewStyle[]}
>
<Image
source={{ uri: imageUri }}
style={[
styles.cardImage,
aspectRatio ? { aspectRatio } : undefined,
].filter(Boolean) as ViewStyle[]}
contentFit="cover"
/>
<LinearGradient
colors={['rgba(17, 17, 17, 0)', 'rgba(17, 17, 17, 0.9)']}
start={{ x: 0, y: 0 }}
end={{ x: 0, y: 1 }}
style={styles.cardImageGradient}
/>
<Text style={styles.cardTitle} numberOfLines={1}>
{title}
</Text>
</View>
</Pressable>
)
}
export const TemplateCard = memo(TemplateCardComponent)
const styles = StyleSheet.create({
card: {
marginBottom: 5,
},
cardImageContainer: {
width: '100%',
borderRadius: 8,
overflow: 'hidden',
position: 'relative',
},
cardImage: {
width: '100%',
borderRadius: 8,
},
cardImageGradient: {
position: 'absolute',
bottom: 0,
left: 0,
right: 0,
height: '50%',
},
cardTitle: {
position: 'absolute',
bottom: 8,
left: 8,
right: 8,
color: '#F5F5F5',
fontSize: 12,
fontWeight: '500',
},
})