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:
90
hooks/use-sticky-tabs.ts
Normal file
90
hooks/use-sticky-tabs.ts
Normal file
@@ -0,0 +1,90 @@
|
||||
import { useCallback, useRef, useState } from 'react'
|
||||
|
||||
export interface UseStickyTabsReturn {
|
||||
isSticky: boolean
|
||||
tabsHeight: number
|
||||
tabsPositionRef: React.MutableRefObject<number>
|
||||
titleBarHeightRef: React.MutableRefObject<number>
|
||||
handleScroll: (scrollY: number) => void
|
||||
handleTabsLayout: (y: number, height: number) => void
|
||||
handleTitleBarLayout: (height: number) => void
|
||||
}
|
||||
|
||||
/**
|
||||
* Hook for managing sticky tabs state and scroll monitoring
|
||||
*
|
||||
* This hook extracts the sticky tabs logic from the home page,
|
||||
* providing a reusable way to handle tab stickiness based on scroll position.
|
||||
*
|
||||
* @returns {UseStickyTabsReturn} Object containing sticky state and handlers
|
||||
*
|
||||
* @example
|
||||
* ```tsx
|
||||
* const {
|
||||
* isSticky,
|
||||
* tabsHeight,
|
||||
* handleScroll,
|
||||
* handleTabsLayout,
|
||||
* handleTitleBarLayout
|
||||
* } = useStickyTabs()
|
||||
*
|
||||
* // In ScrollView
|
||||
* <ScrollView
|
||||
* onScroll={(e) => handleScroll(e.nativeEvent.contentOffset.y)}
|
||||
* >
|
||||
* <View onLayout={(e) => handleTitleBarLayout(e.nativeEvent.layout.height)}>
|
||||
* <TitleBar />
|
||||
* </View>
|
||||
* <View onLayout={(e) => handleTabsLayout(
|
||||
* e.nativeEvent.layout.y,
|
||||
* e.nativeEvent.layout.height
|
||||
* )}>
|
||||
* <Tabs />
|
||||
* </View>
|
||||
* </ScrollView>
|
||||
*
|
||||
* // Sticky tabs overlay
|
||||
* {isSticky && (
|
||||
* <View style={{ position: 'absolute', top: 0, height: tabsHeight }}>
|
||||
* <Tabs />
|
||||
* </View>
|
||||
* )}
|
||||
* ```
|
||||
*/
|
||||
export function useStickyTabs(): UseStickyTabsReturn {
|
||||
const [isSticky, setIsSticky] = useState(false)
|
||||
const [tabsHeight, setTabsHeight] = useState(0)
|
||||
const tabsPositionRef = useRef(0)
|
||||
const titleBarHeightRef = useRef(0)
|
||||
|
||||
const handleScroll = useCallback((scrollY: number) => {
|
||||
if (scrollY > tabsPositionRef.current) {
|
||||
if (!isSticky) {
|
||||
setIsSticky(true)
|
||||
}
|
||||
} else {
|
||||
if (isSticky) {
|
||||
setIsSticky(false)
|
||||
}
|
||||
}
|
||||
}, [isSticky])
|
||||
|
||||
const handleTabsLayout = useCallback((y: number, height: number) => {
|
||||
tabsPositionRef.current = y
|
||||
setTabsHeight(height)
|
||||
}, [])
|
||||
|
||||
const handleTitleBarLayout = useCallback((height: number) => {
|
||||
titleBarHeightRef.current = height
|
||||
}, [])
|
||||
|
||||
return {
|
||||
isSticky,
|
||||
tabsHeight,
|
||||
tabsPositionRef,
|
||||
titleBarHeightRef,
|
||||
handleScroll,
|
||||
handleTabsLayout,
|
||||
handleTitleBarLayout,
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user