mirror of
https://github.com/getpaseo/paseo.git
synced 2026-10-23 09:42:53 +00:00
An iOS pan waiting for manual activation can deliver finger-up without finalizing, leaving the row active and the outer scroller locked. Fail a pending pan when its touch stream ends so the existing finalization path cancels the drag. Track activation immediately so an active drag keeps its normal completion path. Based on the touch-release diagnosis in #5586. The native regression fails before the fix and passes on the Namespace iOS simulator. Co-authored-by: Kassam Khoja <kassam@shopwrk.com>
476 lines
19 KiB
Diff
476 lines
19 KiB
Diff
diff --git a/node_modules/react-native-draggable-flatlist/lib/typescript/components/DraggableFlatList.d.ts b/node_modules/react-native-draggable-flatlist/lib/typescript/components/DraggableFlatList.d.ts
|
|
index d3a4f1f..083bf19 100644
|
|
--- a/node_modules/react-native-draggable-flatlist/lib/typescript/components/DraggableFlatList.d.ts
|
|
+++ b/node_modules/react-native-draggable-flatlist/lib/typescript/components/DraggableFlatList.d.ts
|
|
@@ -16,12 +16,14 @@ declare const _default: <T>(props: Omit<FlatListProps<T>, "keyExtractor" | "data
|
|
keyExtractor: (item: T, index: number) => string;
|
|
onDragBegin?: ((index: number) => void) | undefined;
|
|
onDragEnd?: ((params: import("../types").DragEndParams<T>) => void) | undefined;
|
|
+ onDragTerminate?: (() => void) | undefined;
|
|
onPlaceholderIndexChange?: ((placeholderIndex: number) => void) | undefined;
|
|
onRelease?: ((index: number) => void) | undefined;
|
|
onScrollOffsetChange?: ((scrollOffset: number) => void) | undefined;
|
|
renderItem: import("../types").RenderItem<T>;
|
|
renderPlaceholder?: import("../types").RenderPlaceholder<T> | undefined;
|
|
simultaneousHandlers?: React.Ref<any> | React.Ref<any>[] | undefined;
|
|
+ dragGestureHostPresented?: boolean | undefined;
|
|
outerScrollOffset?: Animated.SharedValue<number> | undefined;
|
|
onAnimValInit?: ((animVals: {
|
|
activeCellOffset: import("react-native-reanimated").SharedValue<number>;
|
|
diff --git a/node_modules/react-native-draggable-flatlist/lib/typescript/components/NestableDraggableFlatList.d.ts b/node_modules/react-native-draggable-flatlist/lib/typescript/components/NestableDraggableFlatList.d.ts
|
|
index d3c6cbb..36e9645 100644
|
|
--- a/node_modules/react-native-draggable-flatlist/lib/typescript/components/NestableDraggableFlatList.d.ts
|
|
+++ b/node_modules/react-native-draggable-flatlist/lib/typescript/components/NestableDraggableFlatList.d.ts
|
|
@@ -15,12 +15,14 @@ export declare const NestableDraggableFlatList: <T>(props: Omit<import("react-na
|
|
keyExtractor: (item: T, index: number) => string;
|
|
onDragBegin?: ((index: number) => void) | undefined;
|
|
onDragEnd?: ((params: import("../types").DragEndParams<T>) => void) | undefined;
|
|
+ onDragTerminate?: (() => void) | undefined;
|
|
onPlaceholderIndexChange?: ((placeholderIndex: number) => void) | undefined;
|
|
onRelease?: ((index: number) => void) | undefined;
|
|
onScrollOffsetChange?: ((scrollOffset: number) => void) | undefined;
|
|
renderItem: import("../types").RenderItem<T>;
|
|
renderPlaceholder?: import("../types").RenderPlaceholder<T> | undefined;
|
|
simultaneousHandlers?: React.Ref<any> | React.Ref<any>[] | undefined;
|
|
+ dragGestureHostPresented?: boolean | undefined;
|
|
outerScrollOffset?: Animated.SharedValue<number> | undefined;
|
|
onAnimValInit?: ((animVals: {
|
|
activeCellOffset: import("react-native-reanimated").SharedValue<number>;
|
|
diff --git a/node_modules/react-native-draggable-flatlist/lib/typescript/types.d.ts b/node_modules/react-native-draggable-flatlist/lib/typescript/types.d.ts
|
|
index 2abb4a2..8ca967f 100644
|
|
--- a/node_modules/react-native-draggable-flatlist/lib/typescript/types.d.ts
|
|
+++ b/node_modules/react-native-draggable-flatlist/lib/typescript/types.d.ts
|
|
@@ -23,12 +23,16 @@ export declare type DraggableFlatListProps<T> = Modify<FlatListProps<T>, {
|
|
keyExtractor: (item: T, index: number) => string;
|
|
onDragBegin?: (index: number) => void;
|
|
onDragEnd?: (params: DragEndParams<T>) => void;
|
|
+ /** @internal Used by the nestable wrapper to release its outer-scroll lock on cancellation. */
|
|
+ onDragTerminate?: () => void;
|
|
onPlaceholderIndexChange?: (placeholderIndex: number) => void;
|
|
onRelease?: (index: number) => void;
|
|
onScrollOffsetChange?: (scrollOffset: number) => void;
|
|
renderItem: RenderItem<T>;
|
|
renderPlaceholder?: RenderPlaceholder<T>;
|
|
simultaneousHandlers?: React.Ref<any> | React.Ref<any>[];
|
|
+ /** @internal Forces handler reattachment when a retained native host is presented. */
|
|
+ dragGestureHostPresented?: boolean;
|
|
outerScrollOffset?: Animated.SharedValue<number>;
|
|
onAnimValInit?: (animVals: ReturnType<typeof useAnimatedValues>) => void;
|
|
itemEnteringAnimation?: AnimateProps<Animated.View>["entering"];
|
|
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 7c88afc..a74140f 100644
|
|
--- a/node_modules/react-native-draggable-flatlist/src/components/DraggableFlatList.tsx
|
|
+++ b/node_modules/react-native-draggable-flatlist/src/components/DraggableFlatList.tsx
|
|
@@ -16,9 +16,11 @@ import {
|
|
FlatList,
|
|
Gesture,
|
|
GestureDetector,
|
|
+ State,
|
|
} from "react-native-gesture-handler";
|
|
import Animated, {
|
|
runOnJS,
|
|
+ runOnUI,
|
|
useAnimatedReaction,
|
|
useAnimatedScrollHandler,
|
|
useSharedValue,
|
|
@@ -92,6 +94,17 @@ function DraggableFlatListInner<T>(props: DraggableFlatListProps<T>) {
|
|
activeCellOffset.value = -1;
|
|
setActiveKey(null);
|
|
});
|
|
+ const terminalHandled = useSharedValue(false);
|
|
+ const gestureSessionId = useSharedValue(0);
|
|
+ const gestureSessionActive = useSharedValue(false);
|
|
+ const currentGestureSessionRef = useRef<{
|
|
+ id: number;
|
|
+ active: boolean;
|
|
+ } | null>(null);
|
|
+ const activeDragRef = useRef<{
|
|
+ sessionId: number;
|
|
+ index: number;
|
|
+ } | null>(null);
|
|
|
|
const {
|
|
dragHitSlop = DEFAULT_PROPS.dragHitSlop,
|
|
@@ -144,19 +157,88 @@ function DraggableFlatListInner<T>(props: DraggableFlatListProps<T>) {
|
|
});
|
|
}, [props.data, keyExtractor, keyToIndexRef]);
|
|
|
|
+ const onDragCancel = useStableCallback((sessionId: number, index: number) => {
|
|
+ if (activeDragRef.current?.sessionId !== sessionId) return;
|
|
+ activeDragRef.current = null;
|
|
+ reset();
|
|
+ props.onRelease?.(index);
|
|
+ props.onDragTerminate?.();
|
|
+ });
|
|
+
|
|
+ useEffect(() => {
|
|
+ return () => {
|
|
+ const activeDrag = activeDragRef.current;
|
|
+ if (!activeDrag) return;
|
|
+ activeDragRef.current = null;
|
|
+ propsRef.current.onRelease?.(activeDrag.index);
|
|
+ propsRef.current.onDragTerminate?.();
|
|
+ };
|
|
+ }, [propsRef]);
|
|
+
|
|
+ const onGestureBegin = useStableCallback((sessionId: number) => {
|
|
+ currentGestureSessionRef.current = { id: sessionId, active: true };
|
|
+ });
|
|
+
|
|
+ const onGestureFinalize = useStableCallback(
|
|
+ (sessionId: number, committed: boolean) => {
|
|
+ if (currentGestureSessionRef.current?.id === sessionId) {
|
|
+ currentGestureSessionRef.current.active = false;
|
|
+ }
|
|
+ if (!committed && activeDragRef.current?.sessionId === sessionId) {
|
|
+ onDragCancel(sessionId, activeDragRef.current.index);
|
|
+ }
|
|
+ }
|
|
+ );
|
|
+
|
|
+ const onDragActivationRejected = useStableCallback((sessionId: number) => {
|
|
+ if (activeDragRef.current?.sessionId === sessionId) {
|
|
+ onDragCancel(sessionId, activeDragRef.current.index);
|
|
+ }
|
|
+ });
|
|
+
|
|
+ const activateDragOnUI = (
|
|
+ sessionId: number,
|
|
+ index: number,
|
|
+ cellOffset: number,
|
|
+ cellSize: number,
|
|
+ hasCellMeasurements: boolean
|
|
+ ) => {
|
|
+ "worklet";
|
|
+ if (
|
|
+ gestureSessionId.value !== sessionId ||
|
|
+ !gestureSessionActive.value ||
|
|
+ terminalHandled.value
|
|
+ ) {
|
|
+ runOnJS(onDragActivationRejected)(sessionId);
|
|
+ return;
|
|
+ }
|
|
+ if (hasCellMeasurements) {
|
|
+ activeCellOffset.value = cellOffset;
|
|
+ activeCellSize.value = cellSize;
|
|
+ }
|
|
+ spacerIndexAnim.value = index;
|
|
+ activeIndexAnim.value = index;
|
|
+ };
|
|
+
|
|
const drag = useStableCallback((activeKey: string) => {
|
|
- if (disabled.value) return;
|
|
+ const gestureSession = currentGestureSessionRef.current;
|
|
+ if (disabled.value || !gestureSession?.active) return;
|
|
const index = keyToIndexRef.current.get(activeKey);
|
|
const cellData = cellDataRef.current.get(activeKey);
|
|
- if (cellData) {
|
|
- activeCellOffset.value = cellData.measurements.offset;
|
|
- activeCellSize.value = cellData.measurements.size;
|
|
- }
|
|
|
|
const { onDragBegin } = propsRef.current;
|
|
if (index !== undefined) {
|
|
- spacerIndexAnim.value = index;
|
|
- activeIndexAnim.value = index;
|
|
+ runOnUI(activateDragOnUI)(
|
|
+ gestureSession.id,
|
|
+ index,
|
|
+ cellData?.measurements.offset ?? -1,
|
|
+ cellData?.measurements.size ?? -1,
|
|
+ Boolean(cellData)
|
|
+ );
|
|
+ activeDragRef.current = {
|
|
+ sessionId: gestureSession.id,
|
|
+ index,
|
|
+ };
|
|
setActiveKey(activeKey);
|
|
onDragBegin?.(index);
|
|
}
|
|
@@ -218,8 +300,25 @@ function DraggableFlatListInner<T>(props: DraggableFlatListProps<T>) {
|
|
props.onRelease?.(index);
|
|
});
|
|
|
|
+ const finalizeCancelledDrag = () => {
|
|
+ "worklet";
|
|
+ const activeIndex = activeIndexAnim.value;
|
|
+ if (terminalHandled.value || activeIndex < 0) return;
|
|
+
|
|
+ terminalHandled.value = true;
|
|
+ isTouchActiveNative.value = false;
|
|
+ touchTranslate.value = 0;
|
|
+ activeIndexAnim.value = -1;
|
|
+ spacerIndexAnim.value = -1;
|
|
+ activeCellSize.value = -1;
|
|
+ activeCellOffset.value = -1;
|
|
+ disabled.value = false;
|
|
+ };
|
|
+
|
|
const onDragEnd = useStableCallback(
|
|
- ({ from, to }: { from: number; to: number }) => {
|
|
+ ({ from, to, sessionId }: { from: number; to: number; sessionId: number }) => {
|
|
+ if (activeDragRef.current?.sessionId !== sessionId) return;
|
|
+ activeDragRef.current = null;
|
|
const { onDragEnd, data } = props;
|
|
|
|
const newData = [...data];
|
|
@@ -238,26 +337,6 @@ function DraggableFlatListInner<T>(props: DraggableFlatListProps<T>) {
|
|
props.onPlaceholderIndexChange?.(index);
|
|
});
|
|
|
|
- // Handle case where user ends drag without moving their finger.
|
|
- useAnimatedReaction(
|
|
- () => {
|
|
- return isTouchActiveNative.value;
|
|
- },
|
|
- (cur, prev) => {
|
|
- if (cur !== prev && !cur) {
|
|
- const hasMoved = !!touchTranslate.value;
|
|
- if (!hasMoved && activeIndexAnim.value >= 0 && !disabled.value) {
|
|
- runOnJS(onRelease)(activeIndexAnim.value);
|
|
- runOnJS(onDragEnd)({
|
|
- from: activeIndexAnim.value,
|
|
- to: spacerIndexAnim.value,
|
|
- });
|
|
- }
|
|
- }
|
|
- },
|
|
- [isTouchActiveNative, onDragEnd, onRelease]
|
|
- );
|
|
-
|
|
useAnimatedReaction(
|
|
() => {
|
|
return spacerIndexAnim.value;
|
|
@@ -271,11 +350,24 @@ function DraggableFlatListInner<T>(props: DraggableFlatListProps<T>) {
|
|
);
|
|
|
|
const gestureDisabled = useSharedValue(false);
|
|
+ const touchStartX = useSharedValue(0);
|
|
+ const touchStartY = useSharedValue(0);
|
|
+ const manualPanActivated = useSharedValue(false);
|
|
+ const coordinatesDragWithExternalGesture = Boolean(props.simultaneousHandlers);
|
|
|
|
- const panGesture = Gesture.Pan()
|
|
+ let panGesture = Gesture.Pan()
|
|
.onBegin((evt) => {
|
|
+ manualPanActivated.value = false;
|
|
gestureDisabled.value = disabled.value;
|
|
if (gestureDisabled.value) return;
|
|
+ const sessionId = gestureSessionId.value + 1;
|
|
+ gestureSessionId.value = sessionId;
|
|
+ gestureSessionActive.value = true;
|
|
+ terminalHandled.value = false;
|
|
+ runOnJS(onGestureBegin)(sessionId);
|
|
+ panGestureState.value = evt.state;
|
|
+ })
|
|
+ .onStart((evt) => {
|
|
panGestureState.value = evt.state;
|
|
})
|
|
.onUpdate((evt) => {
|
|
@@ -286,8 +378,15 @@ function DraggableFlatListInner<T>(props: DraggableFlatListProps<T>) {
|
|
: evt.translationY;
|
|
touchTranslate.value = translation;
|
|
})
|
|
- .onEnd((evt) => {
|
|
+ .onEnd((evt, success) => {
|
|
if (gestureDisabled.value) return;
|
|
+ if (
|
|
+ !success ||
|
|
+ terminalHandled.value ||
|
|
+ !gestureSessionActive.value
|
|
+ )
|
|
+ return;
|
|
+
|
|
// Set touch val to current translate val
|
|
isTouchActiveNative.value = false;
|
|
const translation = horizontalAnim.value
|
|
@@ -299,6 +398,7 @@ function DraggableFlatListInner<T>(props: DraggableFlatListProps<T>) {
|
|
|
|
// Only call onDragEnd if actually dragging a cell
|
|
if (activeIndexAnim.value === -1 || disabled.value) return;
|
|
+ terminalHandled.value = true;
|
|
disabled.value = true;
|
|
runOnJS(onRelease)(activeIndexAnim.value);
|
|
const springTo = placeholderOffset.value - activeCellOffset.value;
|
|
@@ -309,22 +409,84 @@ function DraggableFlatListInner<T>(props: DraggableFlatListProps<T>) {
|
|
runOnJS(onDragEnd)({
|
|
from: activeIndexAnim.value,
|
|
to: spacerIndexAnim.value,
|
|
+ sessionId: gestureSessionId.value,
|
|
});
|
|
disabled.value = false;
|
|
}
|
|
);
|
|
})
|
|
- .onTouchesDown(() => {
|
|
+ .onFinalize((evt) => {
|
|
+ manualPanActivated.value = false;
|
|
+ panGestureState.value = evt.state;
|
|
+ runOnJS(onContainerTouchEnd)();
|
|
+ if (gestureDisabled.value) return;
|
|
+
|
|
+ const sessionId = gestureSessionId.value;
|
|
+ const committed = terminalHandled.value;
|
|
+ gestureSessionActive.value = false;
|
|
+ if (!committed) finalizeCancelledDrag();
|
|
+ runOnJS(onGestureFinalize)(sessionId, committed);
|
|
+ })
|
|
+ .onTouchesDown((event) => {
|
|
+ const touch = event.changedTouches[0];
|
|
+ if (touch) {
|
|
+ touchStartX.value = touch.absoluteX;
|
|
+ touchStartY.value = touch.absoluteY;
|
|
+ }
|
|
runOnJS(onContainerTouchStart)();
|
|
})
|
|
- .onTouchesUp(() => {
|
|
+ .onTouchesMove((event, stateManager) => {
|
|
+ if (!coordinatesDragWithExternalGesture) return;
|
|
+
|
|
+ // Manual activation arbitrates only while the pan is waiting. Failing or
|
|
+ // reactivating an active recognizer can leave RNGH/Reanimated livelocked.
|
|
+ if (manualPanActivated.value) return;
|
|
+
|
|
+ const touch = event.changedTouches[0];
|
|
+ if (!touch || event.numberOfTouches !== 1) {
|
|
+ stateManager.fail();
|
|
+ return;
|
|
+ }
|
|
+
|
|
+ const deltaX = touch.absoluteX - touchStartX.value;
|
|
+ const deltaY = touch.absoluteY - touchStartY.value;
|
|
+ const absDeltaX = Math.abs(deltaX);
|
|
+ const absDeltaY = Math.abs(deltaY);
|
|
+
|
|
+ if (activeIndexAnim.value < 0) {
|
|
+ if (absDeltaX > 6 || absDeltaY > 6) {
|
|
+ stateManager.fail();
|
|
+ }
|
|
+ return;
|
|
+ }
|
|
+
|
|
+ if (absDeltaX > 10 && absDeltaX > absDeltaY) {
|
|
+ stateManager.fail();
|
|
+ } else if (absDeltaY >= activationDistanceProp && absDeltaY > absDeltaX) {
|
|
+ manualPanActivated.value = true;
|
|
+ stateManager.activate();
|
|
+ }
|
|
+ })
|
|
+ .onTouchesUp((event, stateManager) => {
|
|
// Turning this into a worklet causes timing issues. We want it to run
|
|
// just after the finger lifts.
|
|
runOnJS(onContainerTouchEnd)();
|
|
+ // A manually activated iOS pan can remain BEGAN after the last finger
|
|
+ // lifts. Fail it explicitly so onFinalize cancels the armed row.
|
|
+ if (event.numberOfTouches === 0 && panGestureState.value === State.BEGAN) {
|
|
+ stateManager.fail();
|
|
+ }
|
|
+ })
|
|
+ .onTouchesCancelled((_event, stateManager) => {
|
|
+ if (panGestureState.value === State.BEGAN) {
|
|
+ stateManager.fail();
|
|
+ }
|
|
});
|
|
|
|
if (dragHitSlop) panGesture.hitSlop(dragHitSlop);
|
|
- if (activationDistanceProp) {
|
|
+ if (coordinatesDragWithExternalGesture) {
|
|
+ panGesture = panGesture.manualActivation(true);
|
|
+ } else if (activationDistanceProp) {
|
|
const activeOffset = [-activationDistanceProp, activationDistanceProp];
|
|
if (props.horizontal) {
|
|
panGesture.activeOffsetX(activeOffset);
|
|
@@ -332,6 +494,21 @@ function DraggableFlatListInner<T>(props: DraggableFlatListProps<T>) {
|
|
panGesture.activeOffsetY(activeOffset);
|
|
}
|
|
}
|
|
+ if (props.simultaneousHandlers) {
|
|
+ panGesture.simultaneousWithExternalGesture(
|
|
+ ...(Array.isArray(props.simultaneousHandlers)
|
|
+ ? props.simultaneousHandlers
|
|
+ : [props.simultaneousHandlers])
|
|
+ );
|
|
+ }
|
|
+
|
|
+ // GestureDetector only updates handler config when a retained host reappears
|
|
+ // with the same native tag. Changing the gesture count forces a native
|
|
+ // reattachment without remounting the list subtree.
|
|
+ const detectorGesture =
|
|
+ props.dragGestureHostPresented === false
|
|
+ ? Gesture.Simultaneous(panGesture, Gesture.Manual().enabled(false))
|
|
+ : panGesture;
|
|
|
|
const onScroll = useStableCallback((scrollOffset: number) => {
|
|
props.onScrollOffsetChange?.(scrollOffset);
|
|
@@ -373,8 +550,9 @@ function DraggableFlatListInner<T>(props: DraggableFlatListProps<T>) {
|
|
horizontal={!!props.horizontal}
|
|
layoutAnimationDisabled={layoutAnimationDisabled}
|
|
>
|
|
- <GestureDetector gesture={panGesture}>
|
|
+ <GestureDetector gesture={detectorGesture}>
|
|
<Animated.View
|
|
+ collapsable={false}
|
|
style={props.containerStyle}
|
|
ref={containerRef}
|
|
onLayout={onContainerLayout}
|
|
diff --git a/node_modules/react-native-draggable-flatlist/src/components/NestableDraggableFlatList.tsx b/node_modules/react-native-draggable-flatlist/src/components/NestableDraggableFlatList.tsx
|
|
index 1559352..33eb5de 100644
|
|
--- a/node_modules/react-native-draggable-flatlist/src/components/NestableDraggableFlatList.tsx
|
|
+++ b/node_modules/react-native-draggable-flatlist/src/components/NestableDraggableFlatList.tsx
|
|
@@ -1,5 +1,5 @@
|
|
import React, { useRef, useState } from "react";
|
|
-import { findNodeHandle, LogBox } from "react-native";
|
|
+import { LogBox } from "react-native";
|
|
import Animated, {
|
|
useDerivedValue,
|
|
useSharedValue,
|
|
@@ -48,7 +48,7 @@ function NestableDraggableFlatListInner<T>(
|
|
});
|
|
|
|
const onListContainerLayout = useStableCallback(async ({ containerRef }) => {
|
|
- const nodeHandle = findNodeHandle(scrollableRef.current);
|
|
+ const nodeHandle = scrollableRef.current;
|
|
|
|
const onSuccess = (_x: number, y: number) => {
|
|
listVerticalOffset.value = y;
|
|
@@ -74,6 +74,10 @@ function NestableDraggableFlatListInner<T>(
|
|
}
|
|
);
|
|
|
|
+ const onDragTerminate = useStableCallback(() => {
|
|
+ setOuterScrollEnabled(true);
|
|
+ });
|
|
+
|
|
const onAnimValInit: DraggableFlatListProps<T>["onAnimValInit"] = useStableCallback(
|
|
(params) => {
|
|
setListHoverOffset(params.hoverOffset);
|
|
@@ -95,6 +99,7 @@ function NestableDraggableFlatListInner<T>(
|
|
outerScrollOffset={outerScrollOffset}
|
|
onDragBegin={onDragBegin}
|
|
onDragEnd={onDragEnd}
|
|
+ onDragTerminate={onDragTerminate}
|
|
onAnimValInit={onAnimValInit}
|
|
/>
|
|
);
|
|
diff --git a/node_modules/react-native-draggable-flatlist/src/types.ts b/node_modules/react-native-draggable-flatlist/src/types.ts
|
|
index d6755c8..1b244a6 100644
|
|
--- a/node_modules/react-native-draggable-flatlist/src/types.ts
|
|
+++ b/node_modules/react-native-draggable-flatlist/src/types.ts
|
|
@@ -36,12 +36,16 @@ export type DraggableFlatListProps<T> = Modify<
|
|
keyExtractor: (item: T, index: number) => string;
|
|
onDragBegin?: (index: number) => void;
|
|
onDragEnd?: (params: DragEndParams<T>) => void;
|
|
+ /** @internal Used by the nestable wrapper to release its outer-scroll lock on cancellation. */
|
|
+ onDragTerminate?: () => void;
|
|
onPlaceholderIndexChange?: (placeholderIndex: number) => void;
|
|
onRelease?: (index: number) => void;
|
|
onScrollOffsetChange?: (scrollOffset: number) => void;
|
|
renderItem: RenderItem<T>;
|
|
renderPlaceholder?: RenderPlaceholder<T>;
|
|
simultaneousHandlers?: React.Ref<any> | React.Ref<any>[];
|
|
+ /** @internal Forces handler reattachment when a retained native host is presented. */
|
|
+ dragGestureHostPresented?: boolean;
|
|
outerScrollOffset?: Animated.SharedValue<number>;
|
|
onAnimValInit?: (animVals: ReturnType<typeof useAnimatedValues>) => void;
|
|
itemEnteringAnimation?: AnimateProps<Animated.View>["entering"];
|