-
-
Notifications
You must be signed in to change notification settings - Fork 65
/
Copy pathOverflowMenu.tsx
106 lines (99 loc) · 2.92 KB
/
OverflowMenu.tsx
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
import {
extractOverflowButtonData,
overflowMenuPressHandlerDropdownMenu,
defaultOnOverflowMenuPress,
type OnOverflowMenuPressParams,
} from './overflowMenuPressHandlers';
import { OVERFLOW_TOP, useOverflowMenu } from './OverflowMenuContext';
import { View, StyleSheet, type ColorValue } from 'react-native';
import { HeaderButton, type HeaderButtonProps } from '../HeaderButton';
import { ButtonsWrapper, ButtonsWrapperProps } from '../ButtonsWrapper';
import {
Children,
ComponentType,
isValidElement,
useCallback,
useRef,
type ReactElement,
} from 'react';
import * as React from 'react';
import { OVERFLOW_BUTTON_TEST_ID } from '../e2e';
export type OverflowMenuProps = Omit<
HeaderButtonProps,
'onPress' | 'title' | 'renderButton'
> &
Pick<ButtonsWrapperProps, 'left' | 'preset' | 'children'> & {
OverflowIcon: ReactElement | ComponentType<{ color: ColorValue }>;
onPress?: (params: OnOverflowMenuPressParams) => any;
};
export const OverflowMenu = ({
children,
OverflowIcon = <View />,
accessibilityLabel = 'More options',
testID = OVERFLOW_BUTTON_TEST_ID,
onPress = defaultOnOverflowMenuPress,
left = false, // this is needed only when OverflowMenu is rendered without HeaderButtons,
preset,
...other
}: OverflowMenuProps) => {
const presentationCalls = useOverflowMenu();
const btnRef = useRef<View | null>(null);
const renderButtonElement = useCallback(
({ color }: { color: ColorValue }) => {
return isValidElement<any>(OverflowIcon) ? (
OverflowIcon
) : (
<OverflowIcon color={color} />
);
},
[OverflowIcon]
);
const presentOverflowMenu = useCallback(() => {
const titlesAndOnPresses =
onPress === overflowMenuPressHandlerDropdownMenu
? []
: extractOverflowButtonData(children);
onPress({
children,
hiddenButtons: titlesAndOnPresses,
overflowButtonRef: btnRef.current,
...presentationCalls,
});
}, [children, onPress, presentationCalls]);
const validChildren = Children.toArray(children).filter(isValidElement);
if (validChildren.length === 0) {
return null;
}
return (
<ButtonsWrapper left={left} style={styles.wrapper} preset={preset}>
<View
ref={btnRef}
collapsable={false}
style={styles.overflowMenuAnchor}
/>
<HeaderButton
title="overflow menu"
renderButton={renderButtonElement}
onPress={presentOverflowMenu}
accessibilityLabel={accessibilityLabel}
testID={testID}
{...other}
/>
</ButtonsWrapper>
);
};
const styles = StyleSheet.create({
wrapper: {
columnGap: 0,
},
overflowMenuAnchor: {
// these are really just needed bcs of the native android popup menu
position: 'absolute',
top: -OVERFLOW_TOP,
// TODO android actually has a little gap on the right of the menu
right: -15,
backgroundColor: 'transparent',
width: 1,
height: 1,
},
});