Skip to main content

Active Item Portal

Description

This example demonstrates how to use the active item portal to render the active item on top of other components.

In this case, the PortalProvider is used to render the active item outside of the ScrollView content bounds. This is the only way to render only the active item without cropping it whilst keeping the rest of the grid within the ScrollView content bounds.

Source Code

import { useCallback, useState } from 'react';
import { Pressable, StyleSheet, Text, View } from 'react-native';
import Animated, { useAnimatedRef } from 'react-native-reanimated';
import type { SortableGridRenderItem } from 'react-native-sortables';
import Sortable from 'react-native-sortables';

const DATA = Array.from({ length: 18 }, (_, index) => `Item ${index + 1}`);

export default function Example() {
const [portalEnabled, setPortalEnabled] = useState(false);
const scrollableRef = useAnimatedRef<Animated.ScrollView>();

const renderItem = useCallback<SortableGridRenderItem<string>>(
({ item }) => (
<View style={styles.card}>
<Text style={styles.text}>{item}</Text>
</View>
),
[]
);

return (
<View style={styles.container}>
<Sortable.PortalProvider enabled={portalEnabled}>
<View style={styles.gridContainer}>
<Animated.ScrollView
contentContainerStyle={styles.contentContainer}
ref={scrollableRef}>
<Sortable.Grid
columnGap={10}
columns={3}
data={DATA}
renderItem={renderItem}
rowGap={10}
scrollableRef={scrollableRef}
/>
</Animated.ScrollView>
</View>
<Pressable onPress={() => setPortalEnabled(prev => !prev)}>
<Text
style={
styles.buttonText
}>{`${portalEnabled ? 'Disable' : 'Enable'} Portal`}</Text>
</Pressable>
</Sortable.PortalProvider>
</View>
);
}

const styles = StyleSheet.create({
container: {
flex: 1,
alignItems: 'center',
justifyContent: 'center',
backgroundColor: '#DDE2E3'
},
gridContainer: {
margin: 15,
borderRadius: 10,
height: 400,
backgroundColor: '#FFFFFF'
},
card: {
alignItems: 'center',
backgroundColor: '#36877F',
borderRadius: 10,
height: 100,
justifyContent: 'center'
},
contentContainer: {
padding: 10
},
text: {
color: 'white',
fontWeight: 'bold'
},
buttonText: {
color: '#36877F',
fontSize: 18,
fontWeight: 'bold'
}
});

Result