-
-
Notifications
You must be signed in to change notification settings - Fork 416
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Support multiple DraggableFlatLists within single parent ScrollView (#…
…373) * v3.0.5 * v3.0.6 * v3.0.7 * feat: support nested flatlists * comments * rename nested -> nestable * update export * update README * typo Co-authored-by: computerjazz <[email protected]>
- Loading branch information
1 parent
1779964
commit c8e47e9
Showing
11 changed files
with
539 additions
and
5 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,72 @@ | ||
import React, { useMemo, useRef, useState } from "react"; | ||
import { findNodeHandle, LogBox } from "react-native"; | ||
import Animated, { add } from "react-native-reanimated"; | ||
import DraggableFlatList, { DraggableFlatListProps } from "../index"; | ||
import { useNestableScrollContainerContext } from "../context/nestableScrollContainerContext"; | ||
import { useNestedAutoScroll } from "../hooks/useNestedAutoScroll"; | ||
|
||
export function NestableDraggableFlatList<T>(props: DraggableFlatListProps<T>) { | ||
const hasSuppressedWarnings = useRef(false); | ||
|
||
if (!hasSuppressedWarnings.current) { | ||
LogBox.ignoreLogs([ | ||
"VirtualizedLists should never be nested inside plain ScrollViews with the same orientation because it can break windowing", | ||
]); // Ignore log notification by message | ||
//@ts-ignore | ||
console.reportErrorsAsExceptions = false; | ||
hasSuppressedWarnings.current = true; | ||
} | ||
|
||
const { | ||
containerRef, | ||
outerScrollOffset, | ||
setOuterScrollEnabled, | ||
} = useNestableScrollContainerContext(); | ||
|
||
const listVerticalOffset = useMemo(() => new Animated.Value<number>(0), []); | ||
const viewRef = useRef<Animated.View>(null); | ||
const [animVals, setAnimVals] = useState({}); | ||
|
||
useNestedAutoScroll(animVals); | ||
|
||
const onListContainerLayout = async () => { | ||
const viewNode = viewRef.current; | ||
const nodeHandle = findNodeHandle(containerRef.current); | ||
|
||
const onSuccess = (_x: number, y: number) => { | ||
listVerticalOffset.setValue(y); | ||
}; | ||
const onFail = () => { | ||
console.log("## nested draggable list measure fail"); | ||
}; | ||
//@ts-ignore | ||
viewNode.measureLayout(nodeHandle, onSuccess, onFail); | ||
}; | ||
|
||
return ( | ||
<Animated.View ref={viewRef} onLayout={onListContainerLayout}> | ||
<DraggableFlatList | ||
activationDistance={20} | ||
autoscrollSpeed={50} | ||
scrollEnabled={false} | ||
{...props} | ||
outerScrollOffset={outerScrollOffset} | ||
onDragBegin={(...args) => { | ||
setOuterScrollEnabled(false); | ||
props.onDragBegin?.(...args); | ||
}} | ||
onDragEnd={(...args) => { | ||
props.onDragEnd?.(...args); | ||
setOuterScrollEnabled(true); | ||
}} | ||
onAnimValInit={(animVals) => { | ||
setAnimVals({ | ||
...animVals, | ||
hoverAnim: add(animVals.hoverAnim, listVerticalOffset), | ||
}); | ||
props.onAnimValInit?.(animVals); | ||
}} | ||
/> | ||
</Animated.View> | ||
); | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,61 @@ | ||
import React, { useMemo } from "react"; | ||
import { NativeScrollEvent, ScrollViewProps } from "react-native"; | ||
import { ScrollView } from "react-native-gesture-handler"; | ||
import Animated, { block, set } from "react-native-reanimated"; | ||
import { | ||
NestableScrollContainerProvider, | ||
useNestableScrollContainerContext, | ||
} from "../context/nestableScrollContainerContext"; | ||
|
||
const AnimatedScrollView = Animated.createAnimatedComponent(ScrollView); | ||
|
||
function NestableScrollContainerInner(props: ScrollViewProps) { | ||
const { | ||
outerScrollOffset, | ||
containerRef, | ||
containerSize, | ||
scrollViewSize, | ||
scrollableRef, | ||
outerScrollEnabled, | ||
} = useNestableScrollContainerContext(); | ||
|
||
const onScroll = useMemo( | ||
() => | ||
Animated.event([ | ||
{ | ||
nativeEvent: ({ contentOffset }: NativeScrollEvent) => | ||
block([set(outerScrollOffset, contentOffset.y)]), | ||
}, | ||
]), | ||
[] | ||
); | ||
|
||
return ( | ||
<Animated.View | ||
ref={containerRef} | ||
onLayout={({ nativeEvent: { layout } }) => { | ||
containerSize.setValue(layout.height); | ||
}} | ||
> | ||
<AnimatedScrollView | ||
{...props} | ||
onContentSizeChange={(w, h) => { | ||
scrollViewSize.setValue(h); | ||
props.onContentSizeChange?.(w, h); | ||
}} | ||
scrollEnabled={outerScrollEnabled} | ||
ref={scrollableRef} | ||
scrollEventThrottle={1} | ||
onScroll={onScroll} | ||
/> | ||
</Animated.View> | ||
); | ||
} | ||
|
||
export function NestableScrollContainer(props: ScrollViewProps) { | ||
return ( | ||
<NestableScrollContainerProvider> | ||
<NestableScrollContainerInner {...props} /> | ||
</NestableScrollContainerProvider> | ||
); | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,57 @@ | ||
import React, { useContext, useMemo, useRef, useState } from "react"; | ||
import { ScrollView } from "react-native-gesture-handler"; | ||
import Animated from "react-native-reanimated"; | ||
|
||
type NestableScrollContainerContextVal = ReturnType< | ||
typeof useSetupNestableScrollContextValue | ||
>; | ||
const NestableScrollContainerContext = React.createContext< | ||
NestableScrollContainerContextVal | undefined | ||
>(undefined); | ||
|
||
function useSetupNestableScrollContextValue() { | ||
const [outerScrollEnabled, setOuterScrollEnabled] = useState(true); | ||
const scrollViewSize = useMemo(() => new Animated.Value<number>(0), []); | ||
const scrollableRef = useRef<ScrollView>(null); | ||
const outerScrollOffset = useMemo(() => new Animated.Value<number>(0), []); | ||
const containerRef = useRef<Animated.View>(null); | ||
const containerSize = useMemo(() => new Animated.Value<number>(0), []); | ||
|
||
const contextVal = useMemo( | ||
() => ({ | ||
outerScrollEnabled, | ||
setOuterScrollEnabled, | ||
outerScrollOffset, | ||
scrollViewSize, | ||
scrollableRef, | ||
containerRef, | ||
containerSize, | ||
}), | ||
[outerScrollEnabled] | ||
); | ||
|
||
return contextVal; | ||
} | ||
|
||
export function NestableScrollContainerProvider({ | ||
children, | ||
}: { | ||
children: React.ReactNode; | ||
}) { | ||
const contextVal = useSetupNestableScrollContextValue(); | ||
return ( | ||
<NestableScrollContainerContext.Provider value={contextVal}> | ||
{children} | ||
</NestableScrollContainerContext.Provider> | ||
); | ||
} | ||
|
||
export function useNestableScrollContainerContext() { | ||
const value = useContext(NestableScrollContainerContext); | ||
if (!value) { | ||
throw new Error( | ||
"useNestableScrollContainerContext must be called from within NestableScrollContainerContext Provider!" | ||
); | ||
} | ||
return value; | ||
} |
Oops, something went wrong.