feat: add TemplateGrid and TitleBar components with tests
- Implemented TemplateGrid component for displaying templates in a grid layout. - Added calculateCardWidth helper function for dynamic card sizing. - Created TitleBar component for displaying the title and points with interaction. - Added unit tests for TemplateGrid and TitleBar components to ensure proper functionality. - Introduced useStickyTabs and useTabNavigation hooks with tests for managing sticky tab behavior and navigation logic. - Implemented useTemplateFilter hook for filtering templates based on video content. - Added comprehensive tests for all new hooks and components to validate behavior and edge cases.
This commit is contained in:
94
components/blocks/home/TemplateGrid.tsx
Normal file
94
components/blocks/home/TemplateGrid.tsx
Normal file
@@ -0,0 +1,94 @@
|
||||
import React, { useState, memo } from 'react'
|
||||
import { View, StyleSheet, LayoutChangeEvent } from 'react-native'
|
||||
import { TemplateCard } from './TemplateCard'
|
||||
|
||||
export interface Template {
|
||||
id: string
|
||||
title: string
|
||||
previewUrl?: string
|
||||
webpPreviewUrl?: string
|
||||
coverImageUrl?: string
|
||||
aspectRatio?: string
|
||||
}
|
||||
|
||||
export interface TemplateGridProps {
|
||||
templates: Template[]
|
||||
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)
|
||||
|
||||
// 空数据时返回 null
|
||||
if (!templates || templates.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 }]}>
|
||||
{templates.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',
|
||||
},
|
||||
})
|
||||
Reference in New Issue
Block a user