94 lines
2.4 KiB
TypeScript
94 lines
2.4 KiB
TypeScript
import React, { useState, memo } from 'react'
|
|
import { View, StyleSheet, LayoutChangeEvent } from 'react-native'
|
|
import type { CategoryTemplate } from '@repo/sdk'
|
|
import { TemplateCard } from './TemplateCard'
|
|
|
|
export type Template = CategoryTemplate
|
|
|
|
export interface TemplateGridProps {
|
|
templates: CategoryTemplate[]
|
|
onTemplatePress: (id: string) => void
|
|
numColumns?: number
|
|
horizontalPadding?: number
|
|
cardGap?: number
|
|
}
|
|
|
|
/**
|
|
* 计算卡片宽度
|
|
* @param gridWidth 网格容器宽度
|
|
* @param horizontalPadding 水平内边距
|
|
* @param cardGap 卡片间距
|
|
* @param numColumns 列数
|
|
* @returns 卡片宽度
|
|
*/
|
|
export function calculateCardWidth(
|
|
gridWidth: number,
|
|
horizontalPadding: number,
|
|
cardGap: number,
|
|
numColumns: number
|
|
): number {
|
|
return (gridWidth - horizontalPadding * 2 - cardGap * (numColumns - 1)) / numColumns
|
|
}
|
|
|
|
const TemplateGridComponent: React.FC<TemplateGridProps> = ({
|
|
templates,
|
|
onTemplatePress,
|
|
numColumns = 3,
|
|
horizontalPadding = 16,
|
|
cardGap = 5,
|
|
}) => {
|
|
const [gridWidth, setGridWidth] = useState(0)
|
|
|
|
// 过滤掉没有 id 的模板
|
|
const validTemplates = templates.filter((template): template is CategoryTemplate & { id: string } =>
|
|
!!template.id
|
|
)
|
|
|
|
// 空数据时返回 null
|
|
if (!validTemplates || validTemplates.length === 0) {
|
|
return null
|
|
}
|
|
|
|
const handleLayout = (e: LayoutChangeEvent) => {
|
|
setGridWidth(e.nativeEvent.layout.width)
|
|
}
|
|
|
|
const cardWidth = calculateCardWidth(gridWidth, horizontalPadding, cardGap, numColumns)
|
|
|
|
return (
|
|
<View
|
|
style={[styles.gridContainer, { paddingHorizontal: horizontalPadding }]}
|
|
onLayout={handleLayout}
|
|
>
|
|
<View style={[styles.grid, { gap: cardGap }]}>
|
|
{validTemplates.map((template) => (
|
|
<TemplateCard
|
|
key={template.id}
|
|
id={template.id}
|
|
title={template.title}
|
|
previewUrl={template.previewUrl}
|
|
webpPreviewUrl={template.webpPreviewUrl}
|
|
coverImageUrl={template.coverImageUrl}
|
|
aspectRatio={template.aspectRatio}
|
|
cardWidth={cardWidth}
|
|
onPress={onTemplatePress}
|
|
testID={`template-card-${template.id}`}
|
|
/>
|
|
))}
|
|
</View>
|
|
</View>
|
|
)
|
|
}
|
|
|
|
export const TemplateGrid = memo(TemplateGridComponent)
|
|
|
|
const styles = StyleSheet.create({
|
|
gridContainer: {
|
|
flex: 1,
|
|
},
|
|
grid: {
|
|
flexDirection: 'row',
|
|
flexWrap: 'wrap',
|
|
},
|
|
})
|