BlueWallet/components/TooltipMenu.tsx
2024-06-16 22:26:43 -04:00

147 lines
4.6 KiB
TypeScript

import React, { forwardRef, Ref, useCallback, useMemo } from 'react';
import { Platform, Pressable, TouchableOpacity, View } from 'react-native';
import { ContextMenuView, RenderItem, OnPressMenuItemEventObject, MenuState, IconConfig } from 'react-native-ios-context-menu';
import { MenuView, MenuAction, NativeActionEvent } from '@react-native-menu/menu';
import { ToolTipMenuProps, Action } from './types';
const BaseToolTipMenu = (props: ToolTipMenuProps, ref: Ref<any>) => {
const {
title = '',
isMenuPrimaryAction = false,
renderPreview,
disabled = false,
onPress,
onMenuWillShow,
onMenuWillHide,
buttonStyle,
onPressMenuItem,
children,
isButton = false,
...restProps
} = props;
const mapMenuItemForContextMenuView = useCallback((action: Action) => {
return {
actionKey: action.id.toString(),
actionTitle: action.text,
icon: action.icon?.iconValue ? ({ iconType: 'SYSTEM', iconValue: action.icon.iconValue } as IconConfig) : undefined,
state: action.menuStateOn ? ('on' as MenuState) : ('off' as MenuState),
attributes: action.disabled ? ['disabled'] : [],
};
}, []);
const mapMenuItemForMenuView = useCallback((action: Action): MenuAction => {
return {
id: action.id.toString(),
title: action.text,
image: action.menuStateOn && Platform.OS === 'android' ? 'checkbox_on_background' : action.icon?.iconValue || undefined,
state: action.menuStateOn ? ('on' as MenuState) : undefined,
attributes: { disabled: action.disabled },
};
}, []);
const contextMenuItems = useMemo(() => {
const flattenedActions = props.actions.flat();
return flattenedActions.map(mapMenuItemForContextMenuView);
}, [props.actions, mapMenuItemForContextMenuView]);
const menuViewItemsIOS = useMemo(() => {
return props.actions.map(actionGroup => {
if (Array.isArray(actionGroup)) {
return {
id: actionGroup[0].id.toString(),
title: '',
subactions: actionGroup.map(mapMenuItemForMenuView),
displayInline: true,
};
} else {
return mapMenuItemForMenuView(actionGroup);
}
});
}, [props.actions, mapMenuItemForMenuView]);
const menuViewItemsAndroid = useMemo(() => {
const mergedActions = props.actions.flat();
return mergedActions.map(mapMenuItemForMenuView);
}, [props.actions, mapMenuItemForMenuView]);
const handlePressMenuItemForContextMenuView = useCallback(
(event: OnPressMenuItemEventObject) => {
onPressMenuItem(event.nativeEvent.actionKey);
},
[onPressMenuItem],
);
const handlePressMenuItemForMenuView = useCallback(
({ nativeEvent }: NativeActionEvent) => {
onPressMenuItem(nativeEvent.event);
},
[onPressMenuItem],
);
const renderContextMenuView = () => {
console.debug('ToolTipMenu.tsx rendering: renderContextMenuView');
return (
<ContextMenuView
ref={ref}
lazyPreview
shouldEnableAggressiveCleanup
internalCleanupMode="automatic"
onPressMenuItem={handlePressMenuItemForContextMenuView}
onMenuWillShow={onMenuWillShow}
onMenuWillHide={onMenuWillHide}
useActionSheetFallback={false}
menuConfig={{
menuTitle: title,
menuItems: contextMenuItems,
}}
{...(renderPreview
? {
previewConfig: {
previewType: 'CUSTOM',
backgroundColor: 'white',
},
renderPreview: renderPreview as RenderItem,
}
: {})}
>
{onPress ? (
<Pressable accessibilityRole="button" onPress={onPress} {...restProps}>
{children}
</Pressable>
) : (
children
)}
</ContextMenuView>
);
};
const renderMenuView = () => {
console.debug('ToolTipMenu.tsx rendering: renderMenuView');
return (
<View>
<MenuView
title={title}
isAnchoredToRight
onPressAction={handlePressMenuItemForMenuView}
actions={Platform.OS === 'ios' ? menuViewItemsIOS : menuViewItemsAndroid}
shouldOpenOnLongPress={!isMenuPrimaryAction}
>
{isMenuPrimaryAction || isButton ? (
<TouchableOpacity style={buttonStyle} disabled={disabled} onPress={onPress} {...restProps}>
{children}
</TouchableOpacity>
) : (
children
)}
</MenuView>
</View>
);
};
return Platform.OS === 'ios' && renderPreview ? renderContextMenuView() : renderMenuView();
};
const ToolTipMenu = forwardRef(BaseToolTipMenu);
export default ToolTipMenu;