Issue
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?
Solution
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.
1. The 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
FlatListcomponent which disables scrolling when there is someselectedItemdefined on state.There is a
menucomponent which is only rendered when there is someselectedItemdefined on state. Thismenuwill have the menu items with their actions.
2. The 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.
3. The 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 component is Animated when rendered. It uses
this.menuScaleas value for itswidthandheight.this.menuScalestarts animating (growing) when the uses presses the item (handleLongPress). - This component's position is set to
absolute, so it is easy to use absolute values fortopandleftprops.
4. Finally 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:
- Add haptics feedback (vibrator).
- Add menu items.
Answered By - ofundefined

0 comments:
Post a Comment
Note: Only a member of this blog may post a comment.