From b7fcb6e9515f82c9ae4e6933e7443b05bb53bace Mon Sep 17 00:00:00 2001 From: "David E. Gelhar" Date: Fri, 2 May 2025 10:20:37 -0400 Subject: [PATCH 1/3] DraggableFlatList contstrain drag to not go into footer --- ...list+4.0.1+001+listfooter-constraint.patch | 109 ++++++++++++++++++ 1 file changed, 109 insertions(+) create mode 100644 patches/react-native-draggable-flatlist+4.0.1+001+listfooter-constraint.patch diff --git a/patches/react-native-draggable-flatlist+4.0.1+001+listfooter-constraint.patch b/patches/react-native-draggable-flatlist+4.0.1+001+listfooter-constraint.patch new file mode 100644 index 000000000000..f1d615920b7b --- /dev/null +++ b/patches/react-native-draggable-flatlist+4.0.1+001+listfooter-constraint.patch @@ -0,0 +1,109 @@ +diff --git a/node_modules/react-native-draggable-flatlist/src/components/DraggableFlatList.tsx b/node_modules/react-native-draggable-flatlist/src/components/DraggableFlatList.tsx +index 2f59c7a..8ad017d 100644 +--- a/node_modules/react-native-draggable-flatlist/src/components/DraggableFlatList.tsx ++++ b/node_modules/react-native-draggable-flatlist/src/components/DraggableFlatList.tsx +@@ -51,7 +51,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, +@@ -65,6 +65,7 @@ function DraggableFlatListInner(props: DraggableFlatListProps) { + activeCellSize, + activeIndexAnim, + containerSize, ++ footerHeight, + scrollOffset, + scrollViewSize, + spacerIndexAnim, +@@ -162,6 +163,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); +@@ -357,6 +366,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. +@@ -174,6 +175,7 @@ function useSetupAnimatedValues() { + placeholderOffset, + resetTouchedCell, + scrollOffset, ++ footerHeight, + scrollViewSize, + spacerIndexAnim, + touchPositionDiff, +@@ -197,6 +199,7 @@ function useSetupAnimatedValues() { + placeholderOffset, + resetTouchedCell, + scrollOffset, ++ footerHeight, + scrollViewSize, + spacerIndexAnim, + touchPositionDiff, +diff --git a/node_modules/react-native-draggable-flatlist/src/types.ts b/node_modules/react-native-draggable-flatlist/src/types.ts +index d6755c8..564e3ff 100644 +--- a/node_modules/react-native-draggable-flatlist/src/types.ts ++++ b/node_modules/react-native-draggable-flatlist/src/types.ts +@@ -52,6 +52,7 @@ export type DraggableFlatListProps = Modify< + layout: LayoutChangeEvent["nativeEvent"]["layout"]; + containerRef: React.RefObject; + }) => void; ++ ListFooterComponent?: React.ReactElement; + } & Partial + >; + From c8e7b9585ee1ea8406545eb733b33c13d05969c9 Mon Sep 17 00:00:00 2001 From: "David E. Gelhar" Date: Sat, 3 May 2025 10:36:02 -0400 Subject: [PATCH 2/3] no separate Android/iOS versions of DraggableFlatList --- .../DraggableList/index.android.tsx | 27 ------------------- .../{index.ios.tsx => index.native.tsx} | 0 2 files changed, 27 deletions(-) delete mode 100644 src/components/DraggableList/index.android.tsx rename src/components/DraggableList/{index.ios.tsx => index.native.tsx} (100%) diff --git a/src/components/DraggableList/index.android.tsx b/src/components/DraggableList/index.android.tsx deleted file mode 100644 index f33a69cd6321..000000000000 --- a/src/components/DraggableList/index.android.tsx +++ /dev/null @@ -1,27 +0,0 @@ -import React from 'react'; -import {View} from 'react-native'; -import {NestableDraggableFlatList, NestableScrollContainer} from 'react-native-draggable-flatlist'; -import type {ScrollView} from 'react-native-gesture-handler'; -import useThemeStyles from '@hooks/useThemeStyles'; -import type DraggableListProps from './types'; - -function DraggableList({ListFooterComponent, ...viewProps}: DraggableListProps, ref: React.ForwardedRef) { - const styles = useThemeStyles(); - return ( - - - {React.isValidElement(ListFooterComponent) && {ListFooterComponent}} - - ); -} - -DraggableList.displayName = 'DraggableList'; - -export default React.forwardRef(DraggableList); diff --git a/src/components/DraggableList/index.ios.tsx b/src/components/DraggableList/index.native.tsx similarity index 100% rename from src/components/DraggableList/index.ios.tsx rename to src/components/DraggableList/index.native.tsx From 6f62727464171c8c862c2ca3837d5405afb1ace3 Mon Sep 17 00:00:00 2001 From: "David E. Gelhar" Date: Thu, 8 May 2025 07:28:48 -0400 Subject: [PATCH 3/3] check for horizontal layout; don't wrap empty footer --- ...list+4.0.1+001+listfooter-constraint.patch | 37 ++++++++++--------- 1 file changed, 19 insertions(+), 18 deletions(-) diff --git a/patches/react-native-draggable-flatlist+4.0.1+001+listfooter-constraint.patch b/patches/react-native-draggable-flatlist+4.0.1+001+listfooter-constraint.patch index f1d615920b7b..3925c00cc187 100644 --- a/patches/react-native-draggable-flatlist+4.0.1+001+listfooter-constraint.patch +++ b/patches/react-native-draggable-flatlist+4.0.1+001+listfooter-constraint.patch @@ -1,5 +1,5 @@ diff --git a/node_modules/react-native-draggable-flatlist/src/components/DraggableFlatList.tsx b/node_modules/react-native-draggable-flatlist/src/components/DraggableFlatList.tsx -index 2f59c7a..8ad017d 100644 +index 2f59c7a..a324da2 100644 --- a/node_modules/react-native-draggable-flatlist/src/components/DraggableFlatList.tsx +++ b/node_modules/react-native-draggable-flatlist/src/components/DraggableFlatList.tsx @@ -51,7 +51,7 @@ const AnimatedFlatList = (Animated.createAnimatedComponent( @@ -15,7 +15,7 @@ index 2f59c7a..8ad017d 100644 activeCellSize, activeIndexAnim, containerSize, -+ footerHeight, ++ footerSize, scrollOffset, scrollViewSize, spacerIndexAnim, @@ -23,34 +23,35 @@ index 2f59c7a..8ad017d 100644 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) => { -+ footerHeight.value = layout.height ++ footerSize.value = props.horizontal ? layout.width : layout.height + }; + + const onListContentSizeChange = (w: number, h: number) => { scrollViewSize.value = props.horizontal ? w : h; props.onContentSizeChange?.(w, h); -@@ -357,6 +366,15 @@ function DraggableFlatListInner(props: DraggableFlatListProps) { +@@ -357,6 +366,16 @@ function DraggableFlatListInner(props: DraggableFlatListProps) { props.onViewableItemsChanged?.(info); }); + // Wrap the provided ListFooterComponent to add an onLayout -+ const wrappedListFooterComponent = -+ -+ {ListFooterComponent} -+ ++ const wrappedListFooterComponent = ListFooterComponent ? ++ ++ {ListFooterComponent} ++ ++ : undefined + return ( (props: DraggableFlatListProps) { +@@ -388,6 +407,7 @@ function DraggableFlatListInner(props: DraggableFlatListProps) { scrollEventThrottle={16} simultaneousHandlers={props.simultaneousHandlers} removeClippedSubviews={false} @@ -59,14 +60,14 @@ index 2f59c7a..8ad017d 100644 {!!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 footerSize = useSharedValue(0); const scrollOffset = useSharedValue(0); const scrollInit = useSharedValue(0); @@ -75,7 +76,7 @@ index 6ff1ed5..82e2ff1 100644 const maxTranslateNegative = -activeCellOffset.value; const maxTranslatePositive = - scrollViewSize.value - (activeCellOffset.value + activeCellSize.value); -+ 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. @@ -83,7 +84,7 @@ index 6ff1ed5..82e2ff1 100644 placeholderOffset, resetTouchedCell, scrollOffset, -+ footerHeight, ++ footerSize, scrollViewSize, spacerIndexAnim, touchPositionDiff, @@ -91,7 +92,7 @@ index 6ff1ed5..82e2ff1 100644 placeholderOffset, resetTouchedCell, scrollOffset, -+ footerHeight, ++ footerSize, scrollViewSize, spacerIndexAnim, touchPositionDiff,