react-native-reorderable-list
Version:
Reorderable list for React Native applications, powered by Reanimated
100 lines (99 loc) • 3.8 kB
JavaScript
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