Skip to main content

Enums

Complete enumeration definitions used throughout the library.

DraggableState

Represents the different states a draggable item can be in during its lifecycle.

enum DraggableState {
/** Item is at rest in its original or dropped position */
IDLE = "IDLE",
/** Item is currently being dragged by the user */
DRAGGING = "DRAGGING",
/** Item has been successfully dropped on a valid drop zone */
DROPPED = "DROPPED",
}

Values

IDLE

  • Value: "IDLE"
  • Description: Item is at rest in its original or dropped position. This is the default state when the item is not being interacted with.

DRAGGING

  • Value: "DRAGGING"
  • Description: Item is currently being dragged by the user. The item is actively following the user's finger/cursor movement.

DROPPED

  • Value: "DROPPED"
  • Description: Item has been successfully dropped on a valid drop zone. This state is typically brief, as the item usually transitions back to IDLE after the drop animation completes.

Usage Examples

State Change Handling

import { DraggableState } from 'react-native-reanimated-dnd';

function TaskItem({ task }: { task: TaskData }) {
const [currentState, setCurrentState] = useState<DraggableState>(DraggableState.IDLE);

const handleStateChange = (state: DraggableState) => {
setCurrentState(state);

switch (state) {
case DraggableState.IDLE:
console.log('Task is at rest');
setItemOpacity(1.0);
break;

case DraggableState.DRAGGING:
console.log('Task is being dragged');
setItemOpacity(0.8);
hapticFeedback();
break;

case DraggableState.DROPPED:
console.log('Task was successfully dropped');
showSuccessAnimation();
playDropSound();
break;
}
};

return (
<Draggable
data={task}
onStateChange={handleStateChange}
>
<View style={[
styles.taskItem,
currentState === DraggableState.DRAGGING && styles.dragging,
currentState === DraggableState.DROPPED && styles.dropped
]}>
<Text>{task.title}</Text>
</View>
</Draggable>
);
}

Conditional Rendering Based on State

function DraggableCard({ data }: { data: CardData }) {
const [state, setState] = useState<DraggableState>(DraggableState.IDLE);

const renderStateIndicator = () => {
switch (state) {
case DraggableState.IDLE:
return <Icon name="grip-vertical" color="#666" />;
case DraggableState.DRAGGING:
return <Icon name="move" color="#3b82f6" />;
case DraggableState.DROPPED:
return <Icon name="check" color="#22c55e" />;
default:
return null;
}
};

return (
<Draggable data={data} onStateChange={setState}>
<View style={styles.card}>
<View style={styles.header}>
{renderStateIndicator()}
<Text style={styles.title}>{data.title}</Text>
</View>

{state === DraggableState.DRAGGING && (
<View style={styles.dragOverlay}>
<Text style={styles.dragText}>Release to drop</Text>
</View>
)}

<Text style={styles.content}>{data.content}</Text>
</View>
</Draggable>
);
}

Analytics and State Tracking

function useAnalytics() {
const [analytics, setAnalytics] = useState({
totalDrags: 0,
totalDrops: 0,
averageDragDuration: 0,
stateHistory: [] as Array<{ state: DraggableState; timestamp: number }>
});

const trackStateChange = useCallback((state: DraggableState) => {
const timestamp = Date.now();

setAnalytics(prev => {
const newHistory = [...prev.stateHistory, { state, timestamp }];

// Calculate metrics
let totalDrags = prev.totalDrags;
let totalDrops = prev.totalDrops;
let averageDragDuration = prev.averageDragDuration;

if (state === DraggableState.DRAGGING) {
totalDrags++;
} else if (state === DraggableState.DROPPED) {
totalDrops++;

// Calculate drag duration
const dragStart = newHistory
.slice()
.reverse()
.find(entry => entry.state === DraggableState.DRAGGING);

if (dragStart) {
const duration = timestamp - dragStart.timestamp;
averageDragDuration = (averageDragDuration + duration) / 2;
}
}

return {
totalDrags,
totalDrops,
averageDragDuration,
stateHistory: newHistory.slice(-100) // Keep last 100 entries
};
});
}, []);

return { analytics, trackStateChange };
}

ScrollDirection

Represents the auto-scroll direction during drag operations in sortable lists.

enum ScrollDirection {
None = "none",
Up = "up",
Down = "down",
}

Values

None

  • Value: "none"
  • Description: No auto-scrolling is occurring. The dragged item is not near the edges of the scrollable container.

Up

  • Value: "up"
  • Description: Auto-scrolling upward. The dragged item is near the top edge of the container, triggering automatic upward scrolling.

Down

  • Value: "down"
  • Description: Auto-scrolling downward. The dragged item is near the bottom edge of the container, triggering automatic downward scrolling.

Usage Examples

Auto-Scroll Indicator

import { ScrollDirection } from 'react-native-reanimated-dnd';

function AutoScrollIndicator({ direction }: { direction: ScrollDirection }) {
if (direction === ScrollDirection.None) {
return null;
}

return (
<View style={[
styles.scrollIndicator,
direction === ScrollDirection.Up ? styles.scrollUp : styles.scrollDown
]}>
<Icon
name={direction === ScrollDirection.Up ? "chevron-up" : "chevron-down"}
size={20}
color="#3b82f6"
/>
<Text style={styles.scrollText}>
Auto-scrolling {direction}
</Text>
</View>
);
}

Scroll Direction Monitoring

function SortableListWithIndicator() {
const [scrollDirection, setScrollDirection] = useState<ScrollDirection>(ScrollDirection.None);

const {
autoScroll,
getItemProps,
...listProps
} = useSortableList({
data: items,
itemHeight: 80
});

// Monitor scroll direction changes
useEffect(() => {
const unsubscribe = autoScroll.addListener(({ value }) => {
setScrollDirection(value as ScrollDirection);
});

return unsubscribe;
}, [autoScroll]);

return (
<View style={styles.container}>
<AutoScrollIndicator direction={scrollDirection} />

<Animated.ScrollView {...listProps}>
<View style={{ height: listProps.contentHeight }}>
{items.map((item, index) => {
const itemProps = getItemProps(item, index);
return (
<SortableItem key={itemProps.id} {...itemProps}>
<ItemCard item={item} />
</SortableItem>
);
})}
</View>
</Animated.ScrollView>

{scrollDirection !== ScrollDirection.None && (
<View style={styles.scrollOverlay}>
<Text>Scrolling {scrollDirection}...</Text>
</View>
)}
</View>
);
}

Custom Auto-Scroll Behavior

function CustomSortableList() {
const [scrollDirection, setScrollDirection] = useState<ScrollDirection>(ScrollDirection.None);
const [scrollSpeed, setScrollSpeed] = useState(0);

const handleScrollDirectionChange = useCallback((direction: ScrollDirection) => {
setScrollDirection(direction);

// Adjust scroll speed based on direction
switch (direction) {
case ScrollDirection.None:
setScrollSpeed(0);
break;
case ScrollDirection.Up:
setScrollSpeed(-50); // Negative for upward
break;
case ScrollDirection.Down:
setScrollSpeed(50); // Positive for downward
break;
}

// Provide haptic feedback
if (direction !== ScrollDirection.None) {
hapticFeedback('light');
}
}, []);

const getScrollIndicatorStyle = () => {
const baseStyle = styles.scrollIndicator;

switch (scrollDirection) {
case ScrollDirection.Up:
return [baseStyle, styles.scrollUp, { opacity: 0.8 }];
case ScrollDirection.Down:
return [baseStyle, styles.scrollDown, { opacity: 0.8 }];
default:
return [baseStyle, { opacity: 0 }];
}
};

return (
<View style={styles.container}>
<Animated.View style={getScrollIndicatorStyle()}>
<Text>Auto-scroll: {scrollDirection}</Text>
<Text>Speed: {Math.abs(scrollSpeed)}px/s</Text>
</Animated.View>

{/* Your sortable list implementation */}
</View>
);
}

Scroll Direction Analytics

function useScrollAnalytics() {
const [analytics, setAnalytics] = useState({
totalScrollEvents: 0,
upScrollCount: 0,
downScrollCount: 0,
averageScrollDuration: 0,
scrollHistory: [] as Array<{
direction: ScrollDirection;
timestamp: number;
duration?: number;
}>
});

const trackScrollDirection = useCallback((direction: ScrollDirection) => {
const timestamp = Date.now();

setAnalytics(prev => {
const newHistory = [...prev.scrollHistory];

if (direction === ScrollDirection.None) {
// End of scroll - calculate duration
const lastScroll = newHistory[newHistory.length - 1];
if (lastScroll && !lastScroll.duration) {
lastScroll.duration = timestamp - lastScroll.timestamp;
}
} else {
// Start of new scroll
newHistory.push({ direction, timestamp });
}

// Calculate metrics
const totalScrollEvents = newHistory.length;
const upScrollCount = newHistory.filter(s => s.direction === ScrollDirection.Up).length;
const downScrollCount = newHistory.filter(s => s.direction === ScrollDirection.Down).length;

const completedScrolls = newHistory.filter(s => s.duration);
const averageScrollDuration = completedScrolls.length > 0
? completedScrolls.reduce((sum, s) => sum + (s.duration || 0), 0) / completedScrolls.length
: 0;

return {
totalScrollEvents,
upScrollCount,
downScrollCount,
averageScrollDuration,
scrollHistory: newHistory.slice(-50) // Keep last 50 entries
};
});
}, []);

return { analytics, trackScrollDirection };
}

Type Guards

Utility functions for type checking with enums.

isDraggableState

function isDraggableState(value: any): value is DraggableState {
return Object.values(DraggableState).includes(value);
}

// Usage
function handleUnknownState(state: unknown) {
if (isDraggableState(state)) {
// state is now typed as DraggableState
console.log(`Valid draggable state: ${state}`);
} else {
console.error('Invalid draggable state:', state);
}
}

isScrollDirection

function isScrollDirection(value: any): value is ScrollDirection {
return Object.values(ScrollDirection).includes(value);
}

// Usage
function handleScrollEvent(direction: unknown) {
if (isScrollDirection(direction)) {
// direction is now typed as ScrollDirection
updateScrollIndicator(direction);
} else {
console.error('Invalid scroll direction:', direction);
}
}

Enum Utilities

State Transitions

const VALID_STATE_TRANSITIONS: Record<DraggableState, DraggableState[]> = {
[DraggableState.IDLE]: [DraggableState.DRAGGING],
[DraggableState.DRAGGING]: [DraggableState.DROPPED, DraggableState.IDLE],
[DraggableState.DROPPED]: [DraggableState.IDLE],
};

function isValidStateTransition(from: DraggableState, to: DraggableState): boolean {
return VALID_STATE_TRANSITIONS[from].includes(to);
}

// Usage
function validateStateChange(currentState: DraggableState, newState: DraggableState) {
if (!isValidStateTransition(currentState, newState)) {
console.warn(`Invalid state transition: ${currentState} -> ${newState}`);
return false;
}
return true;
}

Enum Mapping

const STATE_COLORS: Record<DraggableState, string> = {
[DraggableState.IDLE]: '#6b7280',
[DraggableState.DRAGGING]: '#3b82f6',
[DraggableState.DROPPED]: '#22c55e',
};

const SCROLL_ICONS: Record<ScrollDirection, string> = {
[ScrollDirection.None]: 'minus',
[ScrollDirection.Up]: 'chevron-up',
[ScrollDirection.Down]: 'chevron-down',
};

// Usage
function getStateColor(state: DraggableState): string {
return STATE_COLORS[state];
}

function getScrollIcon(direction: ScrollDirection): string {
return SCROLL_ICONS[direction];
}

See Also