From 01262a3bc22d98624354cb223141fe7b78e06e15 Mon Sep 17 00:00:00 2001 From: "David E. Gelhar" Date: Tue, 6 May 2025 07:45:06 -0400 Subject: [PATCH 1/3] constrain dragging to within list, not footer --- src/components/DraggableFlatList.tsx | 21 ++++++++++++++++++++- src/context/animatedValueContext.tsx | 5 ++++- src/types.ts | 1 + 3 files changed, 25 insertions(+), 2 deletions(-) diff --git a/src/components/DraggableFlatList.tsx b/src/components/DraggableFlatList.tsx index 7c88afc5..ad8a4730 100644 --- a/src/components/DraggableFlatList.tsx +++ b/src/components/DraggableFlatList.tsx @@ -56,7 +56,7 @@ const AnimatedFlatList = (Animated.createAnimatedComponent( FlatList ) as unknown) as (props: RNGHFlatListProps) => React.ReactElement; -function DraggableFlatListInner(props: DraggableFlatListProps) { +function DraggableFlatListInner({ListFooterComponent, ...props}: DraggableFlatListProps) { const { cellDataRef, containerRef, @@ -70,6 +70,7 @@ function DraggableFlatListInner(props: DraggableFlatListProps) { activeCellSize, activeIndexAnim, containerSize, + footerHeight, scrollOffset, scrollViewSize, spacerIndexAnim, @@ -170,6 +171,14 @@ function DraggableFlatListInner(props: DraggableFlatListProps) { props.onContainerLayout?.({ layout, containerRef }); }; + // make height of footer available for use in drag contraints + const onFooterLayout = ({ + nativeEvent: { layout }, + }: LayoutChangeEvent) => { + footerHeight.value = layout.height + }; + + const onListContentSizeChange = (w: number, h: number) => { scrollViewSize.value = props.horizontal ? w : h; props.onContentSizeChange?.(w, h); @@ -366,6 +375,15 @@ function DraggableFlatListInner(props: DraggableFlatListProps) { props.onViewableItemsChanged?.(info); }); + // Wrap the provided ListFooterComponent to add an onLayout + const wrappedListFooterComponent = + + {ListFooterComponent} + + return ( (props: DraggableFlatListProps) { scrollEventThrottle={16} simultaneousHandlers={props.simultaneousHandlers} removeClippedSubviews={false} + ListFooterComponent={wrappedListFooterComponent} /> {!!props.onScrollOffsetChange && ( () { const activeCellSize = useSharedValue(0); // Height or width of acctive cell const activeCellOffset = useSharedValue(0); // Distance between active cell and edge of container + const footerHeight = useSharedValue(0); // Height of list footer const scrollOffset = useSharedValue(0); const scrollInit = useSharedValue(0); @@ -118,7 +119,7 @@ function useSetupAnimatedValues() { const maxTranslateNegative = -activeCellOffset.value; const maxTranslatePositive = - scrollViewSize.value - (activeCellOffset.value + activeCellSize.value); + scrollViewSize.value - (activeCellOffset.value + activeCellSize.value + footerHeight.value); // Only constrain the touch position while the finger is on the screen. This allows the active cell // to snap above/below the fold once let go, if the drag ends at the top/bottom of the screen. @@ -175,6 +176,7 @@ function useSetupAnimatedValues() { placeholderOffset, resetTouchedCell, scrollOffset, + footerHeight, scrollViewSize, spacerIndexAnim, touchPositionDiff, @@ -198,6 +200,7 @@ function useSetupAnimatedValues() { placeholderOffset, resetTouchedCell, scrollOffset, + footerHeight, scrollViewSize, spacerIndexAnim, touchPositionDiff, diff --git a/src/types.ts b/src/types.ts index d6755c8f..564e3ff9 100644 --- a/src/types.ts +++ b/src/types.ts @@ -52,6 +52,7 @@ export type DraggableFlatListProps = Modify< layout: LayoutChangeEvent["nativeEvent"]["layout"]; containerRef: React.RefObject; }) => void; + ListFooterComponent?: React.ReactElement; } & Partial >; From 26177650e34154a2f212cee87fa068b45a7eb067 Mon Sep 17 00:00:00 2001 From: David Gelhar Date: Thu, 8 May 2025 06:22:43 -0400 Subject: [PATCH 2/3] change 'footerHeight' to 'footerSize' , for consistency --- src/components/DraggableFlatList.tsx | 4 ++-- src/context/animatedValueContext.tsx | 8 ++++---- 2 files changed, 6 insertions(+), 6 deletions(-) diff --git a/src/components/DraggableFlatList.tsx b/src/components/DraggableFlatList.tsx index ad8a4730..dcd4d3e2 100644 --- a/src/components/DraggableFlatList.tsx +++ b/src/components/DraggableFlatList.tsx @@ -70,7 +70,7 @@ function DraggableFlatListInner({ListFooterComponent, ...props}: DraggableFla activeCellSize, activeIndexAnim, containerSize, - footerHeight, + footerSize, scrollOffset, scrollViewSize, spacerIndexAnim, @@ -175,7 +175,7 @@ function DraggableFlatListInner({ListFooterComponent, ...props}: DraggableFla const onFooterLayout = ({ nativeEvent: { layout }, }: LayoutChangeEvent) => { - footerHeight.value = layout.height + footerSize.value = layout.height }; diff --git a/src/context/animatedValueContext.tsx b/src/context/animatedValueContext.tsx index efa83388..04e4f411 100644 --- a/src/context/animatedValueContext.tsx +++ b/src/context/animatedValueContext.tsx @@ -60,7 +60,7 @@ function useSetupAnimatedValues() { const activeCellSize = useSharedValue(0); // Height or width of acctive cell const activeCellOffset = useSharedValue(0); // Distance between active cell and edge of container - const footerHeight = useSharedValue(0); // Height of list footer + const footerSize = useSharedValue(0); // Height of list footer const scrollOffset = useSharedValue(0); const scrollInit = useSharedValue(0); @@ -119,7 +119,7 @@ function useSetupAnimatedValues() { const maxTranslateNegative = -activeCellOffset.value; const maxTranslatePositive = - scrollViewSize.value - (activeCellOffset.value + activeCellSize.value + footerHeight.value); + scrollViewSize.value - (activeCellOffset.value + activeCellSize.value + footerSize.value); // Only constrain the touch position while the finger is on the screen. This allows the active cell // to snap above/below the fold once let go, if the drag ends at the top/bottom of the screen. @@ -176,7 +176,7 @@ function useSetupAnimatedValues() { placeholderOffset, resetTouchedCell, scrollOffset, - footerHeight, + footerSize, scrollViewSize, spacerIndexAnim, touchPositionDiff, @@ -200,7 +200,7 @@ function useSetupAnimatedValues() { placeholderOffset, resetTouchedCell, scrollOffset, - footerHeight, + footerSize, scrollViewSize, spacerIndexAnim, touchPositionDiff, From 5fef54bc17a4e93fe6b4012ef3f943599a5efe97 Mon Sep 17 00:00:00 2001 From: David Gelhar Date: Thu, 8 May 2025 07:27:13 -0400 Subject: [PATCH 3/3] support horizontal layout --- src/components/DraggableFlatList.tsx | 19 ++++++++++--------- src/context/animatedValueContext.tsx | 2 +- 2 files changed, 11 insertions(+), 10 deletions(-) diff --git a/src/components/DraggableFlatList.tsx b/src/components/DraggableFlatList.tsx index dcd4d3e2..85ef3d00 100644 --- a/src/components/DraggableFlatList.tsx +++ b/src/components/DraggableFlatList.tsx @@ -171,11 +171,11 @@ function DraggableFlatListInner({ListFooterComponent, ...props}: DraggableFla props.onContainerLayout?.({ layout, containerRef }); }; - // make height of footer available for use in drag contraints + // make size of footer available for use in drag contraints const onFooterLayout = ({ nativeEvent: { layout }, }: LayoutChangeEvent) => { - footerSize.value = layout.height + footerSize.value = props.horizontal ? layout.width : layout.height }; @@ -376,13 +376,14 @@ function DraggableFlatListInner({ListFooterComponent, ...props}: DraggableFla }); // Wrap the provided ListFooterComponent to add an onLayout - const wrappedListFooterComponent = - - {ListFooterComponent} - + const wrappedListFooterComponent = ListFooterComponent ? + + {ListFooterComponent} + + : undefined return ( () { const activeCellSize = useSharedValue(0); // Height or width of acctive cell const activeCellOffset = useSharedValue(0); // Distance between active cell and edge of container - const footerSize = useSharedValue(0); // Height of list footer + const footerSize = useSharedValue(0); const scrollOffset = useSharedValue(0); const scrollInit = useSharedValue(0);