react-native-swipe-cards-stack
Version:
A highly customizable, performant swipeable cards stack component for React Native.
141 lines (140 loc) • 4.55 kB
JavaScript
/**
* Default configuration values for the SwipeableCardsStack component
*/
export const DEFAULT_SWIPE_ICONS = {
// New simplified icon system
leftSwipeIcon: null,
rightSwipeIcon: null,
upSwipeIcon: null,
downSwipeIcon: null,
leftSwipeIconStyle: {},
rightSwipeIconStyle: {},
upSwipeIconStyle: {},
downSwipeIconStyle: {},
// Legacy support (deprecated but maintained for backward compatibility)
tickIcon: null,
crossIcon: null,
upIcon: null,
downIcon: null,
leftIcon: null,
rightIcon: null,
// Legacy show/hide controls (deprecated)
showTickIcon: false, // Changed to false by default
showCrossIcon: false, // Changed to false by default
showUpIcon: false,
showDownIcon: false,
showLeftIcon: false,
showRightIcon: false,
// Legacy style overrides (deprecated)
tickIconStyle: {},
crossIconStyle: {},
upIconStyle: {},
downIconStyle: {},
leftIconStyle: {},
rightIconStyle: {},
// Legacy position controls (deprecated)
iconPosition: 'center',
customIconPosition: {},
};
export const DEFAULT_THRESHOLDS = {
horizontal: 120,
vertical: 120,
iconDelay: 30,
rotationThreshold: 15,
velocity: 0.3,
};
export const DEFAULT_ANIMATIONS = {
duration: 300,
easing: undefined,
useNativeDriver: false,
rotationEnabled: true,
scaleEnabled: false,
opacityEnabled: false,
};
export const DEFAULT_GESTURES = {
swipeDirections: ['left', 'right', 'up', 'down'], // All directions enabled by default
enableRotation: true,
enableScale: true,
gestureThreshold: 10,
simultaneousGestures: false,
allowPartialSwipe: true,
partialSwipeReturnDuration: 300,
partialSwipeReturnEasing: null,
};
export const DEFAULT_STACK_BEHAVIOR = {
stackSize: 2,
};
export const DEFAULT_CARD_STYLE = {
borderRadius: 15,
};
/**
* Merge user props with default values
*/
export const mergeWithDefaults = (props) => {
return {
...props,
cardStyle: { ...DEFAULT_CARD_STYLE, ...props.cardStyle },
swipeIcons: { ...DEFAULT_SWIPE_ICONS, ...props.swipeIcons },
thresholds: { ...DEFAULT_THRESHOLDS, ...props.thresholds },
animations: { ...DEFAULT_ANIMATIONS, ...props.animations },
gestures: { ...DEFAULT_GESTURES, ...props.gestures },
stackBehavior: { ...DEFAULT_STACK_BEHAVIOR, ...props.stackBehavior },
};
};
/**
* Utility functions for the component
*/
export const getCardId = (item, index, keyExtractor) => {
if (keyExtractor) {
return keyExtractor(item, index);
}
// Use index as default identifier since items can be any type (string, number, object, etc.)
return index.toString();
};
export const calculateCardStyle = (relativeIndex, isActive, stackBehavior, cardStyle, activeCardStyle, inactiveCardStyle) => {
const baseStyle = [cardStyle]; // cardStyle is always applied to all cards
if (isActive && activeCardStyle) {
baseStyle.push(activeCardStyle);
}
else if (!isActive && inactiveCardStyle) {
baseStyle.push(inactiveCardStyle);
}
// Apply simple stack transformations (only basic Y offset for now)
if (!isActive && relativeIndex > 0) {
baseStyle.push({
transform: [{ translateY: relativeIndex * 10 }],
opacity: 1 - (relativeIndex * 0.1) // Simple opacity reduction
});
}
return baseStyle;
};
export const getDirectionFromGesture = (dx, dy, thresholds) => {
const horizontalThreshold = thresholds.horizontal || 120;
const verticalThreshold = thresholds.vertical || 120;
// Determine primary direction based on larger displacement
if (Math.abs(dx) > Math.abs(dy)) {
// Horizontal movement is primary
if (Math.abs(dx) > horizontalThreshold) {
return dx > 0 ? 'right' : 'left';
}
}
else {
// Vertical movement is primary
if (Math.abs(dy) > verticalThreshold) {
return dy > 0 ? 'down' : 'up';
}
}
return null;
};
export const shouldShowIcon = (direction, displacement, iconDelay) => {
if (!direction)
return false;
return Math.abs(displacement) > iconDelay;
};
export const calculateIconOpacity = (displacement, iconDelay, threshold) => {
const adjustedDisplacement = Math.abs(displacement) - iconDelay;
if (adjustedDisplacement <= 0)
return 0;
const maxOpacityRange = threshold - iconDelay;
return Math.min(adjustedDisplacement / maxOpacityRange, 1);
};