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
FlatList
component which disables scrolling when there is someselectedItem
defined on state.There is a
menu
component which is only rendered when there is someselectedItem
defined on state. Thismenu
will 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.menuScale
as value for itswidth
andheight
.this.menuScale
starts 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 fortop
andleft
props.
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.