REF: Update tooltip
This commit is contained in:
parent
adc52b53c7
commit
2172d4a20b
@ -1,6 +1,6 @@
|
||||
import React, { useCallback, useMemo, useRef } from 'react';
|
||||
import { Platform, Pressable, StyleSheet, ViewStyle } from 'react-native';
|
||||
import { MenuView, MenuAction, NativeActionEvent } from '@react-native-menu/menu';
|
||||
import ContextMenu, { ContextMenuAction } from 'react-native-context-menu-view';
|
||||
import { ToolTipMenuProps, Action } from './types';
|
||||
import { useSettings } from '../hooks/context/useSettings';
|
||||
|
||||
@ -23,79 +23,73 @@ const ToolTipMenu = (props: ToolTipMenuProps) => {
|
||||
onMenuWillShow,
|
||||
onMenuWillHide,
|
||||
enableAndroidRipple = true,
|
||||
enableIOSPressOpacity = false,
|
||||
} = props;
|
||||
|
||||
const { language } = useSettings();
|
||||
const openedRef = useRef(false);
|
||||
const menuRef = useRef<any>(null);
|
||||
|
||||
const normalizeMenuState = useCallback((menuState?: Action['menuState']): MenuAction['state'] | undefined => {
|
||||
const handleMenuWillShow = useCallback(() => {
|
||||
if (openedRef.current) {
|
||||
return;
|
||||
}
|
||||
openedRef.current = true;
|
||||
onMenuWillShow?.();
|
||||
}, [onMenuWillShow]);
|
||||
|
||||
const normalizeMenuState = useCallback((menuState?: Action['menuState']): boolean | undefined => {
|
||||
if (menuState === undefined) {
|
||||
return undefined;
|
||||
}
|
||||
if (menuState === 'mixed') {
|
||||
return 'mixed';
|
||||
return true;
|
||||
}
|
||||
return menuState ? 'on' : 'off';
|
||||
}, []);
|
||||
|
||||
const buildAttributes = useCallback((action: Action): MenuAction['attributes'] | undefined => {
|
||||
const attributes = {
|
||||
destructive: Boolean(action.destructive),
|
||||
disabled: Boolean(action.disabled),
|
||||
hidden: Boolean(action.hidden),
|
||||
};
|
||||
|
||||
if (!attributes.destructive && !attributes.disabled && !attributes.hidden) {
|
||||
return undefined;
|
||||
}
|
||||
|
||||
return attributes;
|
||||
return Boolean(menuState);
|
||||
}, []);
|
||||
|
||||
const mapMenuItemForMenuView = useCallback(
|
||||
(action: Action): MenuAction | null => {
|
||||
if (!action?.id) return null;
|
||||
(action: Action): ContextMenuAction | null => {
|
||||
if (!action?.id || action.hidden) return null;
|
||||
|
||||
const mappedSubactions = (action.subactions || [])
|
||||
.map(subaction => mapMenuItemForMenuView(subaction))
|
||||
.filter((item): item is MenuAction => item !== null);
|
||||
.filter((item): item is ContextMenuAction => item !== null);
|
||||
|
||||
const menuItem: MenuAction = {
|
||||
id: action.id.toString(),
|
||||
const menuItem: ContextMenuAction = {
|
||||
title: action.text,
|
||||
subtitle: action.subtitle,
|
||||
image: action.icon?.iconValue ?? action.image,
|
||||
imageColor: action.imageColor,
|
||||
attributes: buildAttributes(action),
|
||||
displayInline: Platform.OS === 'ios' ? action.displayInline : undefined,
|
||||
systemIcon: Platform.OS === 'ios' ? action.icon?.iconValue ?? action.image : undefined,
|
||||
icon: Platform.OS === 'android' ? action.icon?.iconValue ?? action.image : undefined,
|
||||
iconColor: typeof action.imageColor === 'string' ? action.imageColor : undefined,
|
||||
destructive: Boolean(action.destructive),
|
||||
disabled: Boolean(action.disabled),
|
||||
inlineChildren: Platform.OS === 'ios' ? action.displayInline : undefined,
|
||||
};
|
||||
|
||||
const state = normalizeMenuState(action.menuState);
|
||||
if (state) {
|
||||
menuItem.state = state;
|
||||
const selected = normalizeMenuState(action.menuState);
|
||||
if (selected !== undefined) {
|
||||
menuItem.selected = selected;
|
||||
}
|
||||
|
||||
if (mappedSubactions.length > 0) {
|
||||
menuItem.subactions = mappedSubactions;
|
||||
menuItem.actions = mappedSubactions;
|
||||
}
|
||||
|
||||
return menuItem;
|
||||
},
|
||||
[buildAttributes, normalizeMenuState],
|
||||
[normalizeMenuState],
|
||||
);
|
||||
|
||||
const menuViewItemsIOS = useMemo(() => {
|
||||
return actions
|
||||
.map(actionGroup => {
|
||||
if (Array.isArray(actionGroup) && actionGroup.length > 0) {
|
||||
const inlineActions = actionGroup.map(mapMenuItemForMenuView).filter((item): item is MenuAction => item !== null);
|
||||
const inlineActions = actionGroup.map(mapMenuItemForMenuView).filter((item): item is ContextMenuAction => item !== null);
|
||||
if (inlineActions.length === 0) return null;
|
||||
const group: MenuAction = {
|
||||
id: inlineActions[0].id,
|
||||
const group: ContextMenuAction = {
|
||||
title: '',
|
||||
subactions: inlineActions,
|
||||
displayInline: true,
|
||||
actions: inlineActions,
|
||||
inlineChildren: true,
|
||||
};
|
||||
return group;
|
||||
}
|
||||
@ -106,18 +100,20 @@ const ToolTipMenu = (props: ToolTipMenuProps) => {
|
||||
|
||||
return null;
|
||||
})
|
||||
.filter((item): item is MenuAction => item !== null);
|
||||
.filter((item): item is ContextMenuAction => item !== null);
|
||||
}, [actions, mapMenuItemForMenuView]);
|
||||
|
||||
const menuViewItemsAndroid = useMemo(() => {
|
||||
const mergedActions = actions.flat().filter(action => action.id);
|
||||
return mergedActions.map(mapMenuItemForMenuView).filter((item): item is MenuAction => item !== null);
|
||||
const mergedActions = actions.flat().filter(action => action.id && !action.hidden);
|
||||
return mergedActions.map(mapMenuItemForMenuView).filter((item): item is ContextMenuAction => item !== null);
|
||||
}, [actions, mapMenuItemForMenuView]);
|
||||
|
||||
const handlePressMenuItemForMenuView = ({ nativeEvent }: NativeActionEvent) => {
|
||||
if (nativeEvent?.event) {
|
||||
onPressMenuItem(nativeEvent.event);
|
||||
const handlePressMenuItemForMenuView = ({ nativeEvent }: { nativeEvent: { name?: string } }) => {
|
||||
if (nativeEvent?.name) {
|
||||
onPressMenuItem(nativeEvent.name);
|
||||
}
|
||||
openedRef.current = false;
|
||||
onMenuWillHide?.();
|
||||
};
|
||||
|
||||
const renderMenuView = () => {
|
||||
@ -137,12 +133,18 @@ const ToolTipMenu = (props: ToolTipMenuProps) => {
|
||||
base.push(buttonStyle);
|
||||
}
|
||||
}
|
||||
if (pressed && enableAndroidRipple) base.push(styles.pressed);
|
||||
// Keep visual feedback on Android by default. iOS context-menu preview
|
||||
// already applies a system press effect; opt in when needed.
|
||||
const shouldApplyPressedStyle =
|
||||
pressed &&
|
||||
((Platform.OS === 'android' && enableAndroidRipple) || (Platform.OS === 'ios' && enableIOSPressOpacity));
|
||||
if (shouldApplyPressedStyle) base.push(styles.pressed);
|
||||
return base;
|
||||
}}
|
||||
disabled={disabled}
|
||||
onPress={onPress}
|
||||
onLongPress={shouldOpenOnLongPress ? () => {} : undefined}
|
||||
onPressIn={!shouldOpenOnLongPress ? handleMenuWillShow : undefined}
|
||||
onLongPress={shouldOpenOnLongPress ? handleMenuWillShow : undefined}
|
||||
accessibilityLabel={accessibilityLabel}
|
||||
accessibilityHint={accessibilityHint}
|
||||
accessibilityRole={accessibilityRole}
|
||||
@ -151,28 +153,24 @@ const ToolTipMenu = (props: ToolTipMenuProps) => {
|
||||
testID={testID}
|
||||
hitSlop={8}
|
||||
>
|
||||
<MenuView
|
||||
ref={menuRef}
|
||||
<ContextMenu
|
||||
title={title}
|
||||
isAnchoredToRight
|
||||
onOpenMenu={() => {
|
||||
openedRef.current = true;
|
||||
onMenuWillShow?.();
|
||||
}}
|
||||
onCloseMenu={() => {
|
||||
previewBackgroundColor="transparent"
|
||||
onPress={handlePressMenuItemForMenuView}
|
||||
onCancel={() => {
|
||||
if (!openedRef.current) {
|
||||
return;
|
||||
}
|
||||
openedRef.current = false;
|
||||
onMenuWillHide?.();
|
||||
}}
|
||||
onPressAction={handlePressMenuItemForMenuView}
|
||||
actions={Platform.OS === 'ios' ? menuViewItemsIOS : menuViewItemsAndroid}
|
||||
shouldOpenOnLongPress={shouldOpenOnLongPress}
|
||||
dropdownMenuMode={!shouldOpenOnLongPress}
|
||||
disabled={disabled}
|
||||
style={buttonStyle ? styles.menuViewFlex : undefined}
|
||||
>
|
||||
{children}
|
||||
</MenuView>
|
||||
</ContextMenu>
|
||||
</Pressable>
|
||||
);
|
||||
};
|
||||
|
||||
@ -452,7 +452,7 @@ export const TransactionListItem: React.FC<TransactionListItemProps> = memo(
|
||||
accessibilityLabel={`${transactionTypeLabel}, ${amountWithUnit}, ${subtitle ?? title}`}
|
||||
accessibilityRole="button"
|
||||
>
|
||||
{/* @ts-ignore - MenuView types can be overly strict about child element props */}
|
||||
{/* @ts-ignore - Context menu wrapper types can be overly strict about child element props */}
|
||||
<ListItem
|
||||
leftAvatar={avatar}
|
||||
title={listTitle}
|
||||
|
||||
@ -22,6 +22,7 @@ export interface ToolTipMenuProps {
|
||||
actions: Action[] | Action[][];
|
||||
children: React.ReactNode;
|
||||
enableAndroidRipple?: boolean;
|
||||
enableIOSPressOpacity?: boolean;
|
||||
dismissMenu?: () => void;
|
||||
onPressMenuItem: (id: string) => void;
|
||||
title?: string;
|
||||
|
||||
@ -1987,6 +1987,8 @@ PODS:
|
||||
- ReactCommon/turbomodule/core
|
||||
- SocketRocket
|
||||
- Yoga
|
||||
- react-native-context-menu-view (1.21.0):
|
||||
- React
|
||||
- react-native-document-picker (12.0.1):
|
||||
- boost
|
||||
- DoubleConversion
|
||||
@ -2045,34 +2047,6 @@ PODS:
|
||||
- ReactCommon/turbomodule/core
|
||||
- SocketRocket
|
||||
- Yoga
|
||||
- react-native-menu (2.0.0):
|
||||
- boost
|
||||
- DoubleConversion
|
||||
- fast_float
|
||||
- fmt
|
||||
- glog
|
||||
- hermes-engine
|
||||
- RCT-Folly
|
||||
- RCT-Folly/Fabric
|
||||
- RCTRequired
|
||||
- RCTTypeSafety
|
||||
- React-Core
|
||||
- React-debug
|
||||
- React-Fabric
|
||||
- React-featureflags
|
||||
- React-graphics
|
||||
- React-ImageManager
|
||||
- React-jsi
|
||||
- React-NativeModulesApple
|
||||
- React-RCTFabric
|
||||
- React-renderercss
|
||||
- React-rendererdebug
|
||||
- React-utils
|
||||
- ReactCodegen
|
||||
- ReactCommon/turbomodule/bridging
|
||||
- ReactCommon/turbomodule/core
|
||||
- SocketRocket
|
||||
- Yoga
|
||||
- react-native-notifications (5.2.2):
|
||||
- React-Core
|
||||
- react-native-safe-area-context (5.7.0):
|
||||
@ -3354,10 +3328,10 @@ DEPENDENCIES:
|
||||
- react-native-blue-crypto (from `../node_modules/react-native-blue-crypto`)
|
||||
- react-native-bw-file-access (from `../blue_modules/react-native-bw-file-access`)
|
||||
- react-native-capture-protection (from `../node_modules/react-native-capture-protection`)
|
||||
- react-native-context-menu-view (from `../node_modules/react-native-context-menu-view`)
|
||||
- "react-native-document-picker (from `../node_modules/@react-native-documents/picker`)"
|
||||
- react-native-get-random-values (from `../node_modules/react-native-get-random-values`)
|
||||
- react-native-image-picker (from `../node_modules/react-native-image-picker`)
|
||||
- "react-native-menu (from `../node_modules/@react-native-menu/menu`)"
|
||||
- react-native-notifications (from `../node_modules/react-native-notifications`)
|
||||
- react-native-safe-area-context (from `../node_modules/react-native-safe-area-context`)
|
||||
- react-native-secure-key-store (from `../node_modules/react-native-secure-key-store`)
|
||||
@ -3534,14 +3508,14 @@ EXTERNAL SOURCES:
|
||||
:path: "../blue_modules/react-native-bw-file-access"
|
||||
react-native-capture-protection:
|
||||
:path: "../node_modules/react-native-capture-protection"
|
||||
react-native-context-menu-view:
|
||||
:path: "../node_modules/react-native-context-menu-view"
|
||||
react-native-document-picker:
|
||||
:path: "../node_modules/@react-native-documents/picker"
|
||||
react-native-get-random-values:
|
||||
:path: "../node_modules/react-native-get-random-values"
|
||||
react-native-image-picker:
|
||||
:path: "../node_modules/react-native-image-picker"
|
||||
react-native-menu:
|
||||
:path: "../node_modules/@react-native-menu/menu"
|
||||
react-native-notifications:
|
||||
:path: "../node_modules/react-native-notifications"
|
||||
react-native-safe-area-context:
|
||||
@ -3683,7 +3657,7 @@ SPEC CHECKSUMS:
|
||||
FBLazyVector: 309703e71d3f2f1ed7dc7889d58309c9d77a95a4
|
||||
fmt: a40bb5bd0294ea969aaaba240a927bd33d878cdd
|
||||
glog: 5683914934d5b6e4240e497e0f4a3b42d1854183
|
||||
hermes-engine: 1195b00d0f49a367b01a38c87b43227542fc3532
|
||||
hermes-engine: b6ee973c4fd6366874d9aabcaf396d53475038eb
|
||||
lottie-ios: 8f959969761e9c45d70353667d00af0e5b9cadb3
|
||||
lottie-react-native: 983fd0489530e8d40f173de7f04e2f88b9317a15
|
||||
RCT-Folly: 846fda9475e61ec7bcbf8a3fe81edfcaeb090669
|
||||
@ -3726,10 +3700,10 @@ SPEC CHECKSUMS:
|
||||
react-native-blue-crypto: de5babd59b17fbf3fc31d2e1e5d59ec859093fbc
|
||||
react-native-bw-file-access: fe925b77dbf48500df0b294c6851f8c84607a203
|
||||
react-native-capture-protection: 6d8d6a714114decb938d029f88e8df3da636e996
|
||||
react-native-context-menu-view: da39a4612c1294d651907162529cfba8421e9b89
|
||||
react-native-document-picker: dc2d83366e47e89e7c51e8a41eab99c1d54e941c
|
||||
react-native-get-random-values: d16467cf726c618e9c7a8c3c39c31faa2244bbba
|
||||
react-native-image-picker: 0314366753615115fa55c3cc937ac44cb7e75702
|
||||
react-native-menu: 8d2c831a735c9e6528c28b26ca14e57591d87e14
|
||||
react-native-notifications: e2d3c022d6077de7e420ba5c01b4bd9464f3941d
|
||||
react-native-safe-area-context: befb5404eb8a16fdc07fa2bebab3568ecabcbb8a
|
||||
react-native-secure-key-store: eb45b44bdec3f48e9be5cdfca0f49ddf64892ea6
|
||||
|
||||
21
package-lock.json
generated
21
package-lock.json
generated
@ -25,7 +25,6 @@
|
||||
"@react-native-community/cli-platform-android": "20.0.2",
|
||||
"@react-native-community/cli-platform-ios": "20.0.2",
|
||||
"@react-native-documents/picker": "12.0.1",
|
||||
"@react-native-menu/menu": "2.0.0",
|
||||
"@react-native-vector-icons/entypo": "12.4.2",
|
||||
"@react-native-vector-icons/fontawesome": "12.4.2",
|
||||
"@react-native-vector-icons/fontawesome6": "12.3.2",
|
||||
@ -76,6 +75,7 @@
|
||||
"react-native-blue-crypto": "github:BlueWallet/react-native-blue-crypto#3cb5442",
|
||||
"react-native-camera-kit-no-google": "16.2.0",
|
||||
"react-native-capture-protection": "github:BlueWallet/react-native-capture-protection#bb78a40",
|
||||
"react-native-context-menu-view": "github:BlueWallet/react-native-context-menu-view#main",
|
||||
"react-native-default-preference": "https://github.com/BlueWallet/react-native-default-preference.git#6338a1f1235e4130b8cfc2dd3b53015eeff2870c",
|
||||
"react-native-device-info": "14.1.1",
|
||||
"react-native-draggable-flatlist": "4.0.3",
|
||||
@ -3624,16 +3624,6 @@
|
||||
"react-native": ">=0.79.0"
|
||||
}
|
||||
},
|
||||
"node_modules/@react-native-menu/menu": {
|
||||
"version": "2.0.0",
|
||||
"resolved": "https://registry.npmjs.org/@react-native-menu/menu/-/menu-2.0.0.tgz",
|
||||
"integrity": "sha512-hb8Mirw6aKPGONhgo52IiNpwHtISVrgCT3rMdFX1qS7eOFNzOcQh8d2UDnaH5zVpxN+QuvWtaaiRMGFpIjzdtA==",
|
||||
"license": "MIT",
|
||||
"peerDependencies": {
|
||||
"react": "*",
|
||||
"react-native": "*"
|
||||
}
|
||||
},
|
||||
"node_modules/@react-native-vector-icons/common": {
|
||||
"version": "12.4.1",
|
||||
"resolved": "https://registry.npmjs.org/@react-native-vector-icons/common/-/common-12.4.1.tgz",
|
||||
@ -15989,6 +15979,15 @@
|
||||
}
|
||||
}
|
||||
},
|
||||
"node_modules/react-native-context-menu-view": {
|
||||
"version": "1.21.0",
|
||||
"resolved": "git+ssh://git@github.com/BlueWallet/react-native-context-menu-view.git#144110b02afdb11b431741aef5da95e91b942a9b",
|
||||
"license": "MIT",
|
||||
"peerDependencies": {
|
||||
"react": "^16.8.1 || ^17.0.0 || ^18.0.0 || ^19.0.0",
|
||||
"react-native": ">=0.60.0-rc.0 <1.0.x"
|
||||
}
|
||||
},
|
||||
"node_modules/react-native-default-preference": {
|
||||
"version": "1.5.1",
|
||||
"resolved": "git+ssh://git@github.com/BlueWallet/react-native-default-preference.git#6338a1f1235e4130b8cfc2dd3b53015eeff2870c",
|
||||
|
||||
@ -107,7 +107,7 @@
|
||||
"@react-native-community/cli-platform-android": "20.0.2",
|
||||
"@react-native-community/cli-platform-ios": "20.0.2",
|
||||
"@react-native-documents/picker": "12.0.1",
|
||||
"@react-native-menu/menu": "2.0.0",
|
||||
"react-native-context-menu-view": "github:BlueWallet/react-native-context-menu-view#main",
|
||||
"@react-native-vector-icons/entypo": "12.4.2",
|
||||
"@react-native-vector-icons/fontawesome": "12.4.2",
|
||||
"@react-native-vector-icons/fontawesome6": "12.3.2",
|
||||
|
||||
@ -1,8 +1,23 @@
|
||||
const path = require('path');
|
||||
|
||||
module.exports = {
|
||||
project: {
|
||||
android: {},
|
||||
ios: {},
|
||||
},
|
||||
dependencies: {
|
||||
'react-native-context-menu-view': {
|
||||
root: path.resolve(__dirname, 'node_modules/react-native-context-menu-view'),
|
||||
platforms: {
|
||||
ios: {
|
||||
podspecPath: path.resolve(__dirname, 'node_modules/react-native-context-menu-view/react-native-context-menu-view.podspec'),
|
||||
},
|
||||
android: {
|
||||
sourceDir: path.resolve(__dirname, 'node_modules/react-native-context-menu-view/android'),
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
codegenConfig: {
|
||||
name: 'BlueWalletSpec',
|
||||
type: 'all',
|
||||
|
||||
Loading…
Reference in New Issue
Block a user