/* eslint-disable react-hooks/exhaustive-deps */ import React, { useCallback, useMemo, useRef, FC } from 'react'; import type { CollapsibleHandles } from './types'; import { CollapsibleContext } from './hooks/useCollapsibleContext'; import { InternalCollapsibleContext } from './hooks/useInternalCollapsibleContext'; import { useDerivedValue, useSharedValue } from 'react-native-reanimated'; import type { LayoutRectangle, View } from 'react-native'; export default function withCollapsibleContext(Component: FC) { return (props: T) => { const collapsibleHandlers = useRef(); const headerHeight = useSharedValue(0); const scrollY = useSharedValue(0); const fixedHeaderHeight = useSharedValue(0); const containerHeight = useSharedValue(0); const scrollViewRef = useRef(null); const containerRef = useRef(null); const headerContainerLayouts = useRef< Record >({}); const headerViewPositions = useSharedValue({}); const setCollapsibleHandlers = useCallback((handlers) => { collapsibleHandlers.current = handlers; }, []); const headerCollapsed = useDerivedValue(() => { const maxY = fixedHeaderHeight.value; return scrollY.value >= maxY; }, []); const contentMinHeight = useDerivedValue(() => { return containerHeight.value + fixedHeaderHeight.value; }, []); const handleHeaderContainerLayout = useCallback( (key: string, layout?: LayoutRectangle, stickyHeight?: number) => { headerContainerLayouts.current[key] = layout ? { ...layout, stickyHeight, } : undefined; const headerContainers = Object.keys( headerContainerLayouts.current ).filter((k: string) => !!headerContainerLayouts.current[k]); // Calculate header positions const sortedHeaders = headerContainers.sort((a, b) => { return ( (headerContainerLayouts.current[a]?.y || 0) - (headerContainerLayouts.current[b]?.y || 0) ); }); const values: any = {}; let aStickyHeight = 0; for (let index = 0; index < sortedHeaders.length; index++) { const headerKey = sortedHeaders[index]; const sHeight = headerContainerLayouts.current[headerKey]?.stickyHeight ?? 0; values[headerKey] = { top: aStickyHeight, stickyHeight: sHeight, }; aStickyHeight += sHeight; } headerViewPositions.value = values; }, [] ); const handleScrollToView = useCallback( (ref: React.RefObject, animated?: boolean) => { if (!ref.current) { return; } ref.current.measureLayout( containerRef.current, (_left: number, top: number, _width: number, _height: number) => { const headerContainers = Object.keys( headerContainerLayouts.current ).filter((k: string) => { const layout = headerContainerLayouts.current[k]; if (layout) { return layout.y + layout.height < top; } return false; }); const stickyHeightAbove = headerContainers.reduce((acc, key) => { const layout = headerContainerLayouts.current[key]; acc += layout?.stickyHeight ?? 0; return acc; }, 0); collapsibleHandlers.current?.scrollTo( top - stickyHeightAbove, animated ); }, () => {} ); }, [] ); const context = useMemo(() => { return { collapse: (animated?: boolean) => collapsibleHandlers.current?.collapse(animated), expand: () => collapsibleHandlers.current?.expand(), scrollTo: (offset: number, animate?: boolean) => collapsibleHandlers.current?.scrollTo(offset, animate), scrollToIndex: (params: any) => collapsibleHandlers.current?.scrollToIndex(params), scrollToLocation: (params: any) => collapsibleHandlers.current?.scrollToLocation(params), headerHeight, scrollY, headerCollapsed, scrollToView: handleScrollToView, }; }, [scrollY, headerHeight, headerCollapsed, handleScrollToView]); const internalContext = useMemo( () => ({ containerRef, containerHeight, scrollViewRef, handleHeaderContainerLayout, setCollapsibleHandlers, headerHeight, fixedHeaderHeight, contentMinHeight, headerViewPositions, }), [ setCollapsibleHandlers, handleHeaderContainerLayout, headerHeight, fixedHeaderHeight, contentMinHeight, headerViewPositions, ] ); return ( {/** @ts-ignore */} ); }; }