I would like to create something like a context menu for an entry within a FlatList: If the user taps and holds, a central dot appears and a couple of icons or SVG graphics around that dot. When the user moves the finger towards one of the icons (and releases the tap), the respective action is triggered, see the screenshot which illustrates that:

Is there an out-of-the-box component for that? Setting up a modal looks overkill, but are there other alternatives?
I wrote this specially for you: https://snack.expo.io/@alexandrelage/4774e0
It's a boilerplate. There is work to do.
I just don't believe the user should need to drag their finger through the menu items in order to select one option. It sounds anti-accessibility design. Instead, you'd better expect the user to select the menu item normally with good old onPress prop.
render() method looks like this: render() {
return (
<SafeAreaView style={styles.container}>
<FlatList
data={data}
renderItem={this.renderItem}
scrollEnabled={!this.state.selectedItem}
/>
{this.state.selectedItem && this.menu()}
</SafeAreaView>
);
}
There is a FlatList component which disables scrolling when there
is some selectedItem defined on state.
There is a menu component which is only rendered when there is some selectedItem defined on state. This menu will have the menu items with their actions.
renderItem method looks like this: renderItem = ({ item }) => (
<TouchableOpacity
key={item}
style={styles.item}
onLongPress={event => this.handleLongPress(item, event)}>
<Text>Long press list item</Text>
</TouchableOpacity>
);
It calls handleLongPress method when the user presses the item on FlatList. The handleLongPress sets the selectedItem and the pageX and pageY on state.
handleLongPress = (item, event) => {
this.animateMenuScale();
const locationY = event.nativeEvent.pageY;
const locationX = event.nativeEvent.pageX;
this.setState({ selectedItem: { item, locationY, locationX } });
};
This setState will trigger rendering the menu component as follows.
menu component looks like this: menu = () => {
const { selectedItem } = this.state;
return (
<View style={styles.menuBackground}>
<Animated.View
style={[
styles.menu,
{
top: selectedItem.locationY-MENU_RADIUS_END/2,
left: selectedItem.locationX-MENU_RADIUS_END/2,
width: this.menuScale,
height: this.menuScale,
borderRadius: this.menuScale
},
]}
/>
</View>
);
};
this.menuScale as value for its width and height. this.menuScale starts animating (growing) when the uses presses the item (handleLongPress).absolute, so it is easy to use absolute values for top and left props.setTimeout to clear the selectedItem state:This line on shouldComponentUpdate sets a timeout if the selectedItem state exists. This timeout waits for 5 seconds then clears the selectedItem state.
shouldComponentUpdate(nextProps, nextState) {
nextState.selectedItem &&
setTimeout(() => this.setState({ selectedItem: null }), 5000); //Clear after few seconds
return true;
}
I hope it helps.
TODO:
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With