import React, { useState, useRef } from 'react'; import { Modal, View, Text, Pressable, Dimensions, Platform, StyleSheet, FlatList, } from 'react-native'; import { Ionicons } from '@expo/vector-icons'; import { useTheme } from '~/hooks/useTheme'; interface ContextMenuAction { title: string; systemIcon?: string; icon?: keyof typeof Ionicons.glyphMap; destructive?: boolean; disabled?: boolean; } interface ContextMenuProps { actions: ContextMenuAction[]; onPress: (index: number) => void; children: React.ReactElement; } export function ContextMenu({ actions, onPress, children }: ContextMenuProps) { const [visible, setVisible] = useState(false); const [menuPosition, setMenuPosition] = useState({ x: 0, y: 0 }); const childRef = useRef(null); const { colors } = useTheme(); const handleLongPress = () => { childRef.current?.measure((x, y, width, height, pageX, pageY) => { const screenHeight = Dimensions.get('window').height; const menuHeight = actions.length * 50 + 20; // Approximate menu height // Position menu above or below the pressed item based on available space const posY = pageY + height + menuHeight > screenHeight ? pageY - menuHeight : pageY + height; setMenuPosition({ x: pageX, y: posY }); setVisible(true); }); }; const handleActionPress = (index: number) => { setVisible(false); // Small delay to allow modal to close before action setTimeout(() => onPress(index), 100); }; const iconMap: Record = { 'doc.text': 'document-text-outline', 'play.circle': 'play-circle-outline', 'square.and.arrow.up': 'share-outline', tag: 'pricetag-outline', trash: 'trash-outline', }; const renderAction = ({ item, index }: { item: ContextMenuAction; index: number }) => { const iconName = item.icon || (item.systemIcon ? iconMap[item.systemIcon] : undefined); const isDisabled = item.disabled; const isDestructive = item.destructive; return ( !isDisabled && handleActionPress(index)} disabled={isDisabled} className={`flex-row items-center px-4 py-3 ${ index < actions.length - 1 ? `border-b ${colors.border}` : '' }`} style={({ pressed }) => ({ backgroundColor: pressed && !isDisabled ? 'rgba(0, 0, 0, 0.05)' : 'transparent', opacity: isDisabled ? 0.5 : 1, })} > {iconName && ( )} {item.title} ); }; return ( <> {React.cloneElement(children, { onLongPress: handleLongPress, delayLongPress: 500, } as any)} setVisible(false)} > setVisible(false)}> index.toString()} scrollEnabled={false} /> ); } const styles = StyleSheet.create({ backdrop: { ...StyleSheet.absoluteFillObject, }, menu: { position: 'absolute', borderRadius: 12, overflow: 'hidden', ...Platform.select({ ios: { // @ts-ignore - React Native Web supports boxShadow boxShadow: '0px 2px 10px rgba(0, 0, 0, 0.25)', }, android: { elevation: 8, }, }), }, });