UNPKG

react-native-reorderable-list

Version:

Reorderable list for React Native applications, powered by Reanimated

100 lines (99 loc) 3.8 kB
import React, { memo, useCallback, useMemo } from 'react'; import Animated, { Easing, runOnUI, useAnimatedReaction, useAnimatedStyle, useDerivedValue, useSharedValue, withTiming } from 'react-native-reanimated'; import { ReorderableCellContext, ReorderableListContext } from '../contexts'; import { useContext } from '../hooks'; import { applyAnimatedStyles } from './helpers'; export const ReorderableListCell = /*#__PURE__*/memo(({ index, startDrag, children, onLayout, itemOffset, itemSize, dragXY, draggedIndex, animationDuration }) => { const { currentIndex, draggedSize, activeIndex, cellAnimations, itemLayoutAnimation, horizontal } = useContext(ReorderableListContext); const dragHandler = useCallback(() => runOnUI(startDrag)(index), [startDrag, index]); const isActive = activeIndex === index; const contextValue = useMemo(() => ({ index, dragHandler, draggedIndex, isActive }), [index, dragHandler, draggedIndex, isActive]); // Keep separate animated reactions that update itemTranslateXY // otherwise animations might stutter if multiple are triggered // (even in other cells, e.g. released item and reordering cells) const itemTranslateXY = useSharedValue(0); const isActiveCell = useDerivedValue(() => draggedIndex.value === index); useAnimatedReaction(() => dragXY.value, () => { if (index === draggedIndex.value && currentIndex.value >= 0 && draggedIndex.value >= 0) { itemTranslateXY.value = dragXY.value; } }); useAnimatedReaction(() => currentIndex.value, () => { if (index !== draggedIndex.value && currentIndex.value >= 0 && draggedIndex.value >= 0) { const moveUp = currentIndex.value > draggedIndex.value; const startMove = Math.min(draggedIndex.value, currentIndex.value); const endMove = Math.max(draggedIndex.value, currentIndex.value); let newValue = 0; if (index >= startMove && index <= endMove) { newValue = moveUp ? -draggedSize.value : draggedSize.value; } if (newValue !== itemTranslateXY.value) { itemTranslateXY.value = withTiming(newValue, { duration: animationDuration.value, easing: Easing.out(Easing.ease) }); } } }); const animatedStyle = useAnimatedStyle(() => { const translatePropName = horizontal.value ? 'translateX' : 'translateY'; if (isActiveCell.value) { const transform = [{ [translatePropName]: itemTranslateXY.value }]; if (Array.isArray(cellAnimations.transform)) { const customTransform = cellAnimations.transform.map(x => applyAnimatedStyles({}, x)); transform.push(...customTransform); } return applyAnimatedStyles({ transform, zIndex: 999 }, cellAnimations, ['transform']); } return { transform: [{ [translatePropName]: itemTranslateXY.value }], // We set zIndex here due to the following issue: // https://github.com/software-mansion/react-native-reanimated/issues/6681#issuecomment-2514228447 zIndex: 0 }; }); const handleLayout = e => { runOnUI((x, y, width, height) => { itemOffset.value[index] = horizontal.value ? x : y; itemSize.value[index] = horizontal.value ? width : height; })(e.nativeEvent.layout.x, e.nativeEvent.layout.y, e.nativeEvent.layout.width, e.nativeEvent.layout.height); onLayout === null || onLayout === void 0 || onLayout(e); }; return /*#__PURE__*/React.createElement(ReorderableCellContext.Provider, { value: contextValue }, /*#__PURE__*/React.createElement(Animated.View, { style: animatedStyle, onLayout: handleLayout, layout: itemLayoutAnimation.current }, children)); }); //# sourceMappingURL=ReorderableListCell.js.map