react-native-hold-menu-actions 0.1.6

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (219) hide show
  1. package/LICENCE +21 -0
  2. package/README.md +38 -0
  3. package/lib/commonjs/components/backdrop/Backdrop.js +104 -0
  4. package/lib/commonjs/components/backdrop/Backdrop.js.map +1 -0
  5. package/lib/commonjs/components/backdrop/constants.js +14 -0
  6. package/lib/commonjs/components/backdrop/constants.js.map +1 -0
  7. package/lib/commonjs/components/backdrop/index.js +16 -0
  8. package/lib/commonjs/components/backdrop/index.js.map +1 -0
  9. package/lib/commonjs/components/backdrop/styles.js +17 -0
  10. package/lib/commonjs/components/backdrop/styles.js.map +1 -0
  11. package/lib/commonjs/components/flatList/FlatList.js +35 -0
  12. package/lib/commonjs/components/flatList/FlatList.js.map +1 -0
  13. package/lib/commonjs/components/flatList/index.js +16 -0
  14. package/lib/commonjs/components/flatList/index.js.map +1 -0
  15. package/lib/commonjs/components/holdItem/HoldItem.js +369 -0
  16. package/lib/commonjs/components/holdItem/HoldItem.js.map +1 -0
  17. package/lib/commonjs/components/holdItem/index.js +16 -0
  18. package/lib/commonjs/components/holdItem/index.js.map +1 -0
  19. package/lib/commonjs/components/holdItem/styles.js +22 -0
  20. package/lib/commonjs/components/holdItem/styles.js.map +1 -0
  21. package/lib/commonjs/components/holdItem/types.d.js +2 -0
  22. package/lib/commonjs/components/holdItem/types.d.js.map +1 -0
  23. package/lib/commonjs/components/icon/Icon.js +43 -0
  24. package/lib/commonjs/components/icon/Icon.js.map +1 -0
  25. package/lib/commonjs/components/icon/index.js +16 -0
  26. package/lib/commonjs/components/icon/index.js.map +1 -0
  27. package/lib/commonjs/components/menu/Menu.js +57 -0
  28. package/lib/commonjs/components/menu/Menu.js.map +1 -0
  29. package/lib/commonjs/components/menu/MenuItem.js +85 -0
  30. package/lib/commonjs/components/menu/MenuItem.js.map +1 -0
  31. package/lib/commonjs/components/menu/MenuItems.js +35 -0
  32. package/lib/commonjs/components/menu/MenuItems.js.map +1 -0
  33. package/lib/commonjs/components/menu/MenuList.js +121 -0
  34. package/lib/commonjs/components/menu/MenuList.js.map +1 -0
  35. package/lib/commonjs/components/menu/Separator.js +47 -0
  36. package/lib/commonjs/components/menu/Separator.js.map +1 -0
  37. package/lib/commonjs/components/menu/calculations.js +31 -0
  38. package/lib/commonjs/components/menu/calculations.js.map +1 -0
  39. package/lib/commonjs/components/menu/constants.js +21 -0
  40. package/lib/commonjs/components/menu/constants.js.map +1 -0
  41. package/lib/commonjs/components/menu/index.js +16 -0
  42. package/lib/commonjs/components/menu/index.js.map +1 -0
  43. package/lib/commonjs/components/menu/styles.js +77 -0
  44. package/lib/commonjs/components/menu/styles.js.map +1 -0
  45. package/lib/commonjs/components/menu/types.d.js +2 -0
  46. package/lib/commonjs/components/menu/types.d.js.map +1 -0
  47. package/lib/commonjs/components/provider/Provider.js +98 -0
  48. package/lib/commonjs/components/provider/Provider.js.map +1 -0
  49. package/lib/commonjs/components/provider/index.js +16 -0
  50. package/lib/commonjs/components/provider/index.js.map +1 -0
  51. package/lib/commonjs/components/provider/reducer.js +50 -0
  52. package/lib/commonjs/components/provider/reducer.js.map +1 -0
  53. package/lib/commonjs/components/provider/types.d.js +2 -0
  54. package/lib/commonjs/components/provider/types.d.js.map +1 -0
  55. package/lib/commonjs/constants.js +60 -0
  56. package/lib/commonjs/constants.js.map +1 -0
  57. package/lib/commonjs/context/index.js +14 -0
  58. package/lib/commonjs/context/index.js.map +1 -0
  59. package/lib/commonjs/context/internal.js +13 -0
  60. package/lib/commonjs/context/internal.js.map +1 -0
  61. package/lib/commonjs/hooks/index.js +24 -0
  62. package/lib/commonjs/hooks/index.js.map +1 -0
  63. package/lib/commonjs/hooks/useDeviceOrientation.js +38 -0
  64. package/lib/commonjs/hooks/useDeviceOrientation.js.map +1 -0
  65. package/lib/commonjs/hooks/useInternal.js +15 -0
  66. package/lib/commonjs/hooks/useInternal.js.map +1 -0
  67. package/lib/commonjs/index.js +40 -0
  68. package/lib/commonjs/index.js.map +1 -0
  69. package/lib/commonjs/styleGuide.js +39 -0
  70. package/lib/commonjs/styleGuide.js.map +1 -0
  71. package/lib/commonjs/utils/calculations.js +73 -0
  72. package/lib/commonjs/utils/calculations.js.map +1 -0
  73. package/lib/commonjs/utils/validations.js +43 -0
  74. package/lib/commonjs/utils/validations.js.map +1 -0
  75. package/lib/module/components/backdrop/Backdrop.js +83 -0
  76. package/lib/module/components/backdrop/Backdrop.js.map +1 -0
  77. package/lib/module/components/backdrop/constants.js +4 -0
  78. package/lib/module/components/backdrop/constants.js.map +1 -0
  79. package/lib/module/components/backdrop/index.js +2 -0
  80. package/lib/module/components/backdrop/index.js.map +1 -0
  81. package/lib/module/components/backdrop/styles.js +7 -0
  82. package/lib/module/components/backdrop/styles.js.map +1 -0
  83. package/lib/module/components/flatList/FlatList.js +17 -0
  84. package/lib/module/components/flatList/FlatList.js.map +1 -0
  85. package/lib/module/components/flatList/index.js +2 -0
  86. package/lib/module/components/flatList/index.js.map +1 -0
  87. package/lib/module/components/holdItem/HoldItem.js +344 -0
  88. package/lib/module/components/holdItem/HoldItem.js.map +1 -0
  89. package/lib/module/components/holdItem/index.js +2 -0
  90. package/lib/module/components/holdItem/index.js.map +1 -0
  91. package/lib/module/components/holdItem/styles.js +12 -0
  92. package/lib/module/components/holdItem/styles.js.map +1 -0
  93. package/lib/module/components/holdItem/types.d.js +2 -0
  94. package/lib/module/components/holdItem/types.d.js.map +1 -0
  95. package/lib/module/components/icon/Icon.js +26 -0
  96. package/lib/module/components/icon/Icon.js.map +1 -0
  97. package/lib/module/components/icon/index.js +2 -0
  98. package/lib/module/components/icon/index.js.map +1 -0
  99. package/lib/module/components/menu/Menu.js +37 -0
  100. package/lib/module/components/menu/Menu.js.map +1 -0
  101. package/lib/module/components/menu/MenuItem.js +59 -0
  102. package/lib/module/components/menu/MenuItem.js.map +1 -0
  103. package/lib/module/components/menu/MenuItems.js +19 -0
  104. package/lib/module/components/menu/MenuItems.js.map +1 -0
  105. package/lib/module/components/menu/MenuList.js +93 -0
  106. package/lib/module/components/menu/MenuList.js.map +1 -0
  107. package/lib/module/components/menu/Separator.js +29 -0
  108. package/lib/module/components/menu/Separator.js.map +1 -0
  109. package/lib/module/components/menu/calculations.js +17 -0
  110. package/lib/module/components/menu/calculations.js.map +1 -0
  111. package/lib/module/components/menu/constants.js +8 -0
  112. package/lib/module/components/menu/constants.js.map +1 -0
  113. package/lib/module/components/menu/index.js +2 -0
  114. package/lib/module/components/menu/index.js.map +1 -0
  115. package/lib/module/components/menu/styles.js +63 -0
  116. package/lib/module/components/menu/styles.js.map +1 -0
  117. package/lib/module/components/menu/types.d.js +2 -0
  118. package/lib/module/components/menu/types.d.js.map +1 -0
  119. package/lib/module/components/provider/Provider.js +75 -0
  120. package/lib/module/components/provider/Provider.js.map +1 -0
  121. package/lib/module/components/provider/index.js +2 -0
  122. package/lib/module/components/provider/index.js.map +1 -0
  123. package/lib/module/components/provider/reducer.js +38 -0
  124. package/lib/module/components/provider/reducer.js.map +1 -0
  125. package/lib/module/components/provider/types.d.js +2 -0
  126. package/lib/module/components/provider/types.d.js.map +1 -0
  127. package/lib/module/constants.js +37 -0
  128. package/lib/module/constants.js.map +1 -0
  129. package/lib/module/context/index.js +2 -0
  130. package/lib/module/context/index.js.map +1 -0
  131. package/lib/module/context/internal.js +4 -0
  132. package/lib/module/context/internal.js.map +1 -0
  133. package/lib/module/hooks/index.js +3 -0
  134. package/lib/module/hooks/index.js.map +1 -0
  135. package/lib/module/hooks/useDeviceOrientation.js +27 -0
  136. package/lib/module/hooks/useDeviceOrientation.js.map +1 -0
  137. package/lib/module/hooks/useInternal.js +4 -0
  138. package/lib/module/hooks/useInternal.js.map +1 -0
  139. package/lib/module/index.js +5 -0
  140. package/lib/module/index.js.map +1 -0
  141. package/lib/module/styleGuide.js +30 -0
  142. package/lib/module/styleGuide.js.map +1 -0
  143. package/lib/module/utils/calculations.js +51 -0
  144. package/lib/module/utils/calculations.js.map +1 -0
  145. package/lib/module/utils/validations.js +38 -0
  146. package/lib/module/utils/validations.js.map +1 -0
  147. package/lib/typescript/components/backdrop/Backdrop.d.ts +3 -0
  148. package/lib/typescript/components/backdrop/constants.d.ts +2 -0
  149. package/lib/typescript/components/backdrop/index.d.ts +1 -0
  150. package/lib/typescript/components/backdrop/styles.d.ts +10 -0
  151. package/lib/typescript/components/flatList/FlatList.d.ts +5 -0
  152. package/lib/typescript/components/flatList/index.d.ts +2 -0
  153. package/lib/typescript/components/holdItem/HoldItem.d.ts +4 -0
  154. package/lib/typescript/components/holdItem/index.d.ts +2 -0
  155. package/lib/typescript/components/holdItem/styles.d.ts +15 -0
  156. package/lib/typescript/components/holdItem/types.d.ts +131 -0
  157. package/lib/typescript/components/icon/Icon.d.ts +7 -0
  158. package/lib/typescript/components/icon/index.d.ts +1 -0
  159. package/lib/typescript/components/menu/Menu.d.ts +3 -0
  160. package/lib/typescript/components/menu/MenuItem.d.ts +8 -0
  161. package/lib/typescript/components/menu/MenuItems.d.ts +6 -0
  162. package/lib/typescript/components/menu/MenuList.d.ts +3 -0
  163. package/lib/typescript/components/menu/Separator.d.ts +3 -0
  164. package/lib/typescript/components/menu/calculations.d.ts +4 -0
  165. package/lib/typescript/components/menu/constants.d.ts +7 -0
  166. package/lib/typescript/components/menu/index.d.ts +1 -0
  167. package/lib/typescript/components/menu/styles.d.ts +59 -0
  168. package/lib/typescript/components/menu/types.d.ts +28 -0
  169. package/lib/typescript/components/provider/Provider.d.ts +10 -0
  170. package/lib/typescript/components/provider/index.d.ts +2 -0
  171. package/lib/typescript/components/provider/reducer.d.ts +20 -0
  172. package/lib/typescript/components/provider/types.d.ts +33 -0
  173. package/lib/typescript/constants.d.ts +29 -0
  174. package/lib/typescript/context/index.d.ts +1 -0
  175. package/lib/typescript/context/internal.d.ts +16 -0
  176. package/lib/typescript/hooks/index.d.ts +2 -0
  177. package/lib/typescript/hooks/useDeviceOrientation.d.ts +3 -0
  178. package/lib/typescript/hooks/useInternal.d.ts +1 -0
  179. package/lib/typescript/index.d.ts +4 -0
  180. package/lib/typescript/styleGuide.d.ts +28 -0
  181. package/lib/typescript/utils/calculations.d.ts +14 -0
  182. package/lib/typescript/utils/validations.d.ts +3 -0
  183. package/package.json +106 -0
  184. package/src/components/backdrop/Backdrop.tsx +138 -0
  185. package/src/components/backdrop/constants.ts +8 -0
  186. package/src/components/backdrop/index.ts +1 -0
  187. package/src/components/backdrop/styles.ts +8 -0
  188. package/src/components/flatList/FlatList.tsx +23 -0
  189. package/src/components/flatList/index.ts +2 -0
  190. package/src/components/holdItem/HoldItem.tsx +449 -0
  191. package/src/components/holdItem/index.ts +2 -0
  192. package/src/components/holdItem/styles.ts +11 -0
  193. package/src/components/holdItem/types.d.ts +131 -0
  194. package/src/components/icon/Icon.tsx +33 -0
  195. package/src/components/icon/index.ts +1 -0
  196. package/src/components/menu/Menu.tsx +57 -0
  197. package/src/components/menu/MenuItem.tsx +79 -0
  198. package/src/components/menu/MenuItems.tsx +26 -0
  199. package/src/components/menu/MenuList.tsx +151 -0
  200. package/src/components/menu/Separator.tsx +28 -0
  201. package/src/components/menu/calculations.ts +49 -0
  202. package/src/components/menu/constants.ts +9 -0
  203. package/src/components/menu/index.ts +1 -0
  204. package/src/components/menu/styles.ts +64 -0
  205. package/src/components/menu/types.d.ts +28 -0
  206. package/src/components/provider/Provider.tsx +105 -0
  207. package/src/components/provider/index.ts +2 -0
  208. package/src/components/provider/reducer.ts +48 -0
  209. package/src/components/provider/types.d.ts +33 -0
  210. package/src/constants.ts +54 -0
  211. package/src/context/index.ts +1 -0
  212. package/src/context/internal.ts +19 -0
  213. package/src/hooks/index.ts +2 -0
  214. package/src/hooks/useDeviceOrientation.ts +28 -0
  215. package/src/hooks/useInternal.ts +4 -0
  216. package/src/index.ts +4 -0
  217. package/src/styleGuide.ts +31 -0
  218. package/src/utils/calculations.ts +110 -0
  219. package/src/utils/validations.ts +42 -0
@@ -0,0 +1,79 @@
1
+ import React, { useCallback } from 'react';
2
+ import { TouchableOpacity } from 'react-native';
3
+ import Animated, { useAnimatedStyle } from 'react-native-reanimated';
4
+
5
+ import Separator from './Separator';
6
+ import styles from './styles';
7
+
8
+ import { MenuItemProps } from './types';
9
+ import { useInternal } from '../../hooks';
10
+ import { CONTEXT_MENU_STATE } from '../../constants';
11
+ import { BORDER_LIGHT_COLOR, BORDER_DARK_COLOR } from './constants';
12
+ import isEqual from 'lodash.isequal';
13
+ import { getColor } from './calculations';
14
+ import { AnimatedIcon } from '../provider/Provider';
15
+
16
+ // @ts-ignore
17
+ const AnimatedTouchable = Animated.createAnimatedComponent(TouchableOpacity);
18
+
19
+ type MenuItemComponentProps = {
20
+ item: MenuItemProps;
21
+ isLast?: boolean;
22
+ };
23
+
24
+ const MenuItemComponent = ({ item, isLast }: MenuItemComponentProps) => {
25
+ const { state, theme, menuProps } = useInternal();
26
+
27
+ const borderStyles = useAnimatedStyle(() => {
28
+ const borderBottomColor =
29
+ theme.value === 'dark' ? BORDER_DARK_COLOR : BORDER_LIGHT_COLOR;
30
+
31
+ return {
32
+ borderBottomColor,
33
+ borderBottomWidth: isLast ? 0 : 1,
34
+ };
35
+ }, [theme, isLast, item]);
36
+
37
+ const textColor = useAnimatedStyle(() => {
38
+ return { color: getColor(item.isTitle, item.isDestructive, theme.value) };
39
+ }, [theme, item]);
40
+
41
+ const handleOnPress = useCallback(() => {
42
+ if (!item.isTitle) {
43
+ const params = menuProps.value.actionParams[item.text] || [];
44
+ if (item.onPress) item.onPress(...params);
45
+ state.value = CONTEXT_MENU_STATE.END;
46
+ }
47
+ // eslint-disable-next-line react-hooks/exhaustive-deps
48
+ }, [state, item]);
49
+
50
+ return (
51
+ <>
52
+ <AnimatedTouchable
53
+ onPress={handleOnPress}
54
+ activeOpacity={!item.isTitle ? 0.4 : 1}
55
+ style={[styles.menuItem, borderStyles]}
56
+ >
57
+ <Animated.Text
58
+ style={[
59
+ item.isTitle ? styles.menuItemTitleText : styles.menuItemText,
60
+ textColor,
61
+ ]}
62
+ >
63
+ {item.text}
64
+ </Animated.Text>
65
+ {!item.isTitle &&
66
+ item.icon &&
67
+ (AnimatedIcon && typeof item.icon === 'string' ? (
68
+ <AnimatedIcon name={item.icon} size={18} style={textColor} />
69
+ ) : typeof item.icon === 'function' ? (
70
+ item.icon()
71
+ ) : null)}
72
+ </AnimatedTouchable>
73
+ {item.withSeparator && <Separator />}
74
+ </>
75
+ );
76
+ };
77
+
78
+ const MenuItem = React.memo(MenuItemComponent, isEqual);
79
+ export default MenuItem;
@@ -0,0 +1,26 @@
1
+ import React, { memo } from 'react';
2
+
3
+ import MenuItem from './MenuItem';
4
+
5
+ import isEqual from 'lodash.isequal';
6
+ import { MenuItemProps } from './types';
7
+
8
+ const MenuItemsComponent = ({ items }: { items: MenuItemProps[] }) => {
9
+ return (
10
+ <>
11
+ {items.map((item: MenuItemProps, index: number) => {
12
+ return (
13
+ <MenuItem
14
+ key={index}
15
+ item={item}
16
+ isLast={items.length === index + 1}
17
+ />
18
+ );
19
+ })}
20
+ </>
21
+ );
22
+ };
23
+
24
+ const MenuItems = memo(MenuItemsComponent, isEqual);
25
+
26
+ export default MenuItems;
@@ -0,0 +1,151 @@
1
+ import React from 'react';
2
+ import { StyleSheet } from 'react-native';
3
+
4
+ import Animated, {
5
+ runOnJS,
6
+ useAnimatedProps,
7
+ useAnimatedReaction,
8
+ useAnimatedStyle,
9
+ useDerivedValue,
10
+ useSharedValue,
11
+ withSpring,
12
+ withTiming,
13
+ } from 'react-native-reanimated';
14
+
15
+ import {
16
+ calculateMenuHeight,
17
+ menuAnimationAnchor,
18
+ } from '../../utils/calculations';
19
+ // @ts-ignore
20
+ import { BlurView } from '@react-native-community/blur';
21
+
22
+ import MenuItems from './MenuItems';
23
+
24
+ import {
25
+ SPRING_CONFIGURATION_MENU,
26
+ HOLD_ITEM_TRANSFORM_DURATION,
27
+ IS_IOS,
28
+ CONTEXT_MENU_STATE,
29
+ } from '../../constants';
30
+
31
+ import styles from './styles';
32
+ import { MenuItemProps } from './types';
33
+ import { useInternal } from '../../hooks';
34
+ import { deepEqual } from '../../utils/validations';
35
+ import { leftOrRight } from './calculations';
36
+
37
+ const AnimatedView = Animated.createAnimatedComponent(BlurView);
38
+
39
+ const MenuListComponent = () => {
40
+ const { state, theme, menuProps } = useInternal();
41
+
42
+ const [itemList, setItemList] = React.useState<MenuItemProps[]>([]);
43
+
44
+ const menuHeight = useDerivedValue(() => {
45
+ const itemsWithSeparator = menuProps.value.items.filter(
46
+ item => item.withSeparator
47
+ );
48
+ return calculateMenuHeight(
49
+ menuProps.value.items.length,
50
+ itemsWithSeparator.length
51
+ );
52
+ }, [menuProps]);
53
+ const prevList = useSharedValue<MenuItemProps[]>([]);
54
+
55
+ const messageStyles = useAnimatedStyle(() => {
56
+ const itemsWithSeparator = menuProps.value.items.filter(
57
+ item => item.withSeparator
58
+ );
59
+
60
+ const translate = menuAnimationAnchor(
61
+ menuProps.value.anchorPosition,
62
+ menuProps.value.itemWidth,
63
+ menuProps.value.items.length,
64
+ itemsWithSeparator.length
65
+ );
66
+
67
+ const _leftPosition = leftOrRight(menuProps);
68
+
69
+ const menuScaleAnimation = () =>
70
+ state.value === CONTEXT_MENU_STATE.ACTIVE
71
+ ? withSpring(1, SPRING_CONFIGURATION_MENU)
72
+ : withTiming(0, {
73
+ duration: HOLD_ITEM_TRANSFORM_DURATION,
74
+ });
75
+
76
+ const opacityAnimation = () =>
77
+ withTiming(state.value === CONTEXT_MENU_STATE.ACTIVE ? 1 : 0, {
78
+ duration: HOLD_ITEM_TRANSFORM_DURATION,
79
+ });
80
+
81
+ return {
82
+ left: _leftPosition,
83
+ height: menuHeight.value,
84
+ opacity: opacityAnimation(),
85
+ transform: [
86
+ { translateX: translate.beginningTransformations.translateX },
87
+ { translateY: translate.beginningTransformations.translateY },
88
+ {
89
+ scale: menuScaleAnimation(),
90
+ },
91
+ { translateX: translate.endingTransformations.translateX },
92
+ { translateY: translate.endingTransformations.translateY },
93
+ ],
94
+ };
95
+ });
96
+
97
+ const animatedInnerContainerStyle = useAnimatedStyle(() => {
98
+ return {
99
+ backgroundColor:
100
+ theme.value === 'light'
101
+ ? IS_IOS
102
+ ? 'rgba(255, 255, 255, .75)'
103
+ : 'rgba(255, 255, 255, .95)'
104
+ : IS_IOS
105
+ ? 'rgba(0,0,0,0.5)'
106
+ : 'rgba(39, 39, 39, .8)',
107
+ };
108
+ }, [theme]);
109
+
110
+ const animatedProps = useAnimatedProps(() => {
111
+ return { blurType: theme.value };
112
+ }, [theme]);
113
+
114
+ const setter = (items: MenuItemProps[]) => {
115
+ setItemList(items);
116
+ prevList.value = items;
117
+ };
118
+
119
+ useAnimatedReaction(
120
+ () => menuProps.value.items,
121
+ _items => {
122
+ if (!deepEqual(_items, prevList.value)) {
123
+ runOnJS(setter)(_items);
124
+ }
125
+ },
126
+ [menuProps]
127
+ );
128
+
129
+ return (
130
+ <AnimatedView
131
+ blurAmount={100}
132
+ //@ts-ignore
133
+ animatedProps={animatedProps}
134
+ style={[styles.menuContainer, messageStyles]}
135
+ >
136
+ <Animated.View
137
+ style={[
138
+ StyleSheet.absoluteFillObject,
139
+ styles.menuInnerContainer,
140
+ animatedInnerContainerStyle,
141
+ ]}
142
+ >
143
+ <MenuItems items={itemList} />
144
+ </Animated.View>
145
+ </AnimatedView>
146
+ );
147
+ };
148
+
149
+ const MenuList = React.memo(MenuListComponent);
150
+
151
+ export default MenuList;
@@ -0,0 +1,28 @@
1
+ import React, { memo } from 'react';
2
+ import { StyleSheet } from 'react-native';
3
+ import Animated, { useAnimatedStyle } from 'react-native-reanimated';
4
+ import { useInternal } from '../../hooks';
5
+
6
+ import { BORDER_LIGHT_COLOR, BORDER_DARK_COLOR } from './constants';
7
+
8
+ const Separator = () => {
9
+ const { theme } = useInternal();
10
+
11
+ const separatorStyles = useAnimatedStyle(() => {
12
+ return {
13
+ backgroundColor:
14
+ theme.value === 'dark' ? BORDER_DARK_COLOR : BORDER_LIGHT_COLOR,
15
+ };
16
+ }, [theme]);
17
+
18
+ return <Animated.View style={[styles.separator, { ...separatorStyles }]} />;
19
+ };
20
+
21
+ export default memo(Separator);
22
+
23
+ const styles = StyleSheet.create({
24
+ separator: {
25
+ width: '100%',
26
+ height: 8,
27
+ },
28
+ });
@@ -0,0 +1,49 @@
1
+ import Animated from 'react-native-reanimated';
2
+
3
+ import { MENU_WIDTH } from '../../constants';
4
+ import {
5
+ MENU_TEXT_DARK_COLOR,
6
+ MENU_TEXT_DESTRUCTIVE_COLOR_DARK,
7
+ MENU_TEXT_DESTRUCTIVE_COLOR_LIGHT,
8
+ MENU_TEXT_LIGHT_COLOR,
9
+ MENU_TITLE_COLOR,
10
+ } from './constants';
11
+ import type { MenuInternalProps } from './types';
12
+
13
+ export const leftOrRight = (
14
+ menuProps: Animated.SharedValue<MenuInternalProps>
15
+ ) => {
16
+ 'worklet';
17
+
18
+ const anchorPositionHorizontal = menuProps.value.anchorPosition.split('-')[1];
19
+ const itemWidth = menuProps.value.itemWidth;
20
+
21
+ let leftPosition = 0;
22
+ anchorPositionHorizontal === 'right'
23
+ ? (leftPosition = -MENU_WIDTH + itemWidth)
24
+ : anchorPositionHorizontal === 'left'
25
+ ? (leftPosition = 0)
26
+ : (leftPosition =
27
+ -menuProps.value.itemWidth -
28
+ MENU_WIDTH / 2 +
29
+ menuProps.value.itemWidth / 2);
30
+
31
+ return leftPosition;
32
+ };
33
+
34
+ export const getColor = (
35
+ isTitle: boolean | undefined,
36
+ isDestructive: boolean | undefined,
37
+ themeValue: 'light' | 'dark'
38
+ ) => {
39
+ 'worklet';
40
+ return isTitle
41
+ ? MENU_TITLE_COLOR
42
+ : isDestructive
43
+ ? themeValue === 'dark'
44
+ ? MENU_TEXT_DESTRUCTIVE_COLOR_DARK
45
+ : MENU_TEXT_DESTRUCTIVE_COLOR_LIGHT
46
+ : themeValue === 'dark'
47
+ ? MENU_TEXT_DARK_COLOR
48
+ : MENU_TEXT_LIGHT_COLOR;
49
+ };
@@ -0,0 +1,9 @@
1
+ export const BORDER_LIGHT_COLOR = 'rgba(0, 0, 0, 0.1)';
2
+ export const BORDER_DARK_COLOR = 'rgba(255, 255, 255, 0.1)';
3
+
4
+ export const MENU_TITLE_COLOR = 'gray';
5
+ export const MENU_TEXT_LIGHT_COLOR = 'rgba(0, 0, 0, 1)';
6
+ export const MENU_TEXT_DARK_COLOR = 'rgb(255, 255, 255)';
7
+
8
+ export const MENU_TEXT_DESTRUCTIVE_COLOR_LIGHT = 'rgb(255, 59,48)';
9
+ export const MENU_TEXT_DESTRUCTIVE_COLOR_DARK = 'rgb(255, 69,58)';
@@ -0,0 +1 @@
1
+ export { default } from './Menu';
@@ -0,0 +1,64 @@
1
+ import { StyleSheet } from 'react-native';
2
+ import { MENU_WIDTH } from '../../constants';
3
+ import styleGuide from '../../styleGuide';
4
+
5
+ const styles = StyleSheet.create({
6
+ menuWrapper: {
7
+ position: 'absolute',
8
+ left: 0,
9
+ zIndex: 10,
10
+ },
11
+ menuContainer: {
12
+ position: 'absolute',
13
+ top: 0,
14
+ width: MENU_WIDTH,
15
+ borderRadius: styleGuide.spacing * 1.5,
16
+ display: 'flex',
17
+ flexDirection: 'row',
18
+ justifyContent: 'flex-start',
19
+ alignItems: 'flex-start',
20
+ overflow: 'hidden',
21
+ zIndex: 15,
22
+ },
23
+ menuInnerContainer: {
24
+ display: 'flex',
25
+ flexDirection: 'column',
26
+ justifyContent: 'flex-start',
27
+ alignItems: 'center',
28
+ },
29
+ menuItem: {
30
+ width: '100%',
31
+ display: 'flex',
32
+ flexDirection: 'row',
33
+ justifyContent: 'space-between',
34
+ alignItems: 'center',
35
+ paddingHorizontal: styleGuide.spacing * 2,
36
+ paddingVertical: styleGuide.spacing * 1.25,
37
+ },
38
+ border: {
39
+ borderBottomWidth: 1,
40
+ borderBottomColor: 'rgba(255, 255, 255, 0.1)',
41
+ },
42
+ menuItemText: {
43
+ fontSize: styleGuide.typography.callout.fontSize,
44
+ lineHeight: styleGuide.typography.callout.lineHeight,
45
+ textAlign: 'left',
46
+ width: '100%',
47
+ flex: 1,
48
+ },
49
+ menuItemTitleText: {
50
+ fontSize: styleGuide.typography.callout2.fontSize,
51
+ lineHeight: styleGuide.typography.callout2.lineHeight,
52
+ textAlign: 'center',
53
+ width: '100%',
54
+ flex: 1,
55
+ },
56
+ textDark: {
57
+ color: 'black',
58
+ },
59
+ textLight: {
60
+ color: 'white',
61
+ },
62
+ });
63
+
64
+ export default styles;
@@ -0,0 +1,28 @@
1
+ import { TransformOriginAnchorPosition } from '../../utils/calculations';
2
+
3
+ export type MenuItemProps = {
4
+ text: string;
5
+ icon?: string | (() => React.ReactElement);
6
+ onPress?: (...args: any[]) => void;
7
+ isTitle?: boolean;
8
+ isDestructive?: boolean;
9
+ withSeparator?: boolean;
10
+ };
11
+
12
+ export type MenuListProps = {
13
+ items: MenuItemProps[];
14
+ };
15
+
16
+ export type MenuInternalProps = {
17
+ items: MenuItemProps[];
18
+ itemHeight: number;
19
+ itemWidth: number;
20
+ itemY: number;
21
+ itemX: number;
22
+ anchorPosition: TransformOriginAnchorPosition;
23
+ menuHeight: number;
24
+ transformValue: number;
25
+ actionParams: {
26
+ [name: string]: (string | number)[];
27
+ };
28
+ };
@@ -0,0 +1,105 @@
1
+ import React, { memo, useEffect, useMemo } from 'react';
2
+ import { PortalProvider } from '@gorhom/portal';
3
+ import Animated, { useSharedValue, useAnimatedReaction, runOnJS } from 'react-native-reanimated';
4
+ import { GestureHandlerRootView } from 'react-native-gesture-handler';
5
+
6
+ // Components
7
+ import { Backdrop } from '../backdrop';
8
+
9
+ // Utils
10
+ import { InternalContext } from '../../context/internal';
11
+ import { HoldMenuProviderProps } from './types';
12
+ import { StateProps, Action } from './reducer';
13
+ import { CONTEXT_MENU_STATE } from '../../constants';
14
+ import { MenuInternalProps } from '../menu/types';
15
+ import Menu from '../menu';
16
+
17
+ export interface Store {
18
+ state: StateProps;
19
+ dispatch?: React.Dispatch<Action>;
20
+ }
21
+
22
+ export let AnimatedIcon: any;
23
+
24
+ const ProviderComponent = ({
25
+ children,
26
+ theme: selectedTheme,
27
+ iconComponent,
28
+ safeAreaInsets,
29
+ onOpen,
30
+ onClose,
31
+ }: HoldMenuProviderProps) => {
32
+ if (iconComponent)
33
+ AnimatedIcon = Animated.createAnimatedComponent(iconComponent);
34
+
35
+ const state = useSharedValue<CONTEXT_MENU_STATE>(
36
+ CONTEXT_MENU_STATE.UNDETERMINED
37
+ );
38
+ const theme = useSharedValue<'light' | 'dark'>(selectedTheme || 'light');
39
+ const menuProps = useSharedValue<MenuInternalProps>({
40
+ itemHeight: 0,
41
+ itemWidth: 0,
42
+ itemX: 0,
43
+ itemY: 0,
44
+ items: [],
45
+ anchorPosition: 'top-center',
46
+ menuHeight: 0,
47
+ transformValue: 0,
48
+ actionParams: {},
49
+ });
50
+
51
+ useEffect(() => {
52
+ theme.value = selectedTheme || 'light';
53
+ // eslint-disable-next-line react-hooks/exhaustive-deps
54
+ }, [selectedTheme]);
55
+
56
+ useAnimatedReaction(
57
+ () => state.value,
58
+ state => {
59
+ switch (state) {
60
+ case CONTEXT_MENU_STATE.ACTIVE: {
61
+ if (onOpen)
62
+ runOnJS(onOpen)();
63
+ break
64
+ }
65
+ case CONTEXT_MENU_STATE.END: {
66
+ if (onClose)
67
+ runOnJS(onClose)();
68
+ break
69
+ }
70
+ }
71
+ },
72
+ [state]
73
+ );
74
+
75
+ const internalContextVariables = useMemo(
76
+ () => ({
77
+ state,
78
+ theme,
79
+ menuProps,
80
+ safeAreaInsets: safeAreaInsets || {
81
+ top: 0,
82
+ bottom: 0,
83
+ left: 0,
84
+ right: 0,
85
+ },
86
+ }),
87
+ [state, theme, menuProps, safeAreaInsets]
88
+ );
89
+
90
+ return (
91
+ <GestureHandlerRootView style={{ flex: 1 }}>
92
+ <InternalContext.Provider value={internalContextVariables}>
93
+ <PortalProvider>
94
+ {children}
95
+ <Backdrop />
96
+ <Menu />
97
+ </PortalProvider>
98
+ </InternalContext.Provider>
99
+ </GestureHandlerRootView>
100
+ );
101
+ };
102
+
103
+ const Provider = memo(ProviderComponent);
104
+
105
+ export default Provider;
@@ -0,0 +1,2 @@
1
+ export { default } from './Provider';
2
+ export type { HoldMenuProviderProps } from './types';
@@ -0,0 +1,48 @@
1
+ import { CONTEXT_MENU_STATE } from '../../constants';
2
+
3
+ export type StateProps = {
4
+ active: number;
5
+ activeItem: string | null;
6
+ theme: 'light' | 'dark';
7
+ };
8
+
9
+ export enum ActionType {
10
+ Active = 'Active',
11
+ End = 'End',
12
+ Theme = 'Theme',
13
+ }
14
+
15
+ export type Action =
16
+ | { type: ActionType.Active; activeItem: string | null }
17
+ | { type: ActionType.End }
18
+ | { type: ActionType.Theme };
19
+
20
+ export const reducer = (state: StateProps, action: Action): StateProps => {
21
+ switch (action.type) {
22
+ case ActionType.Active:
23
+ return {
24
+ ...state,
25
+ active: CONTEXT_MENU_STATE.ACTIVE,
26
+ activeItem: action.activeItem,
27
+ };
28
+ case ActionType.End:
29
+ return {
30
+ ...state,
31
+ active: CONTEXT_MENU_STATE.END,
32
+ activeItem: null,
33
+ };
34
+ case ActionType.Theme:
35
+ return {
36
+ ...state,
37
+ theme: state.theme === 'dark' ? 'light' : 'dark',
38
+ };
39
+ default:
40
+ return state;
41
+ }
42
+ };
43
+
44
+ export const initialState: StateProps = {
45
+ active: 0,
46
+ activeItem: null,
47
+ theme: 'light',
48
+ };
@@ -0,0 +1,33 @@
1
+ export interface HoldMenuProviderProps {
2
+ /**
3
+ * Theme of hold menu. Effects to backdrop and context menu styles. Optional.
4
+ * @type "light" | "dark"
5
+ * @default "light"
6
+ * @examples
7
+ * theme="light"
8
+ */
9
+ theme?: 'dark' | 'light';
10
+ iconComponent?: any;
11
+ children: React.ReactElement | React.ReactElement[];
12
+
13
+ /**
14
+ * Set this to prevent the menu to be opened under the unsafe area.
15
+ * @type object
16
+ * @default
17
+ * { top: 0, bottom: 0, right: 0, left: 0 }
18
+ * @examples
19
+ * ```
20
+ * const insets = useSafeAreaInsets();
21
+ * safeAreaInsets={insets}
22
+ * ```
23
+ */
24
+ safeAreaInsets: {
25
+ top: number;
26
+ right: number;
27
+ bottom: number;
28
+ left: number;
29
+ };
30
+
31
+ onOpen?: () => void;
32
+ onClose?: () => void;
33
+ }
@@ -0,0 +1,54 @@
1
+ import { Dimensions, Platform } from 'react-native';
2
+
3
+ const HOLD_ITEM_TRANSFORM_DURATION = 150;
4
+ const HOLD_ITEM_SCALE_DOWN_VALUE = 0.95;
5
+ const HOLD_ITEM_SCALE_DOWN_DURATION = 210;
6
+
7
+ const SPRING_CONFIGURATION = {
8
+ damping: 33,
9
+ mass: 1.03,
10
+ stiffness: 500,
11
+ restDisplacementThreshold: 0.001,
12
+ restSpeedThreshold: 0.001,
13
+ };
14
+
15
+ const SPRING_CONFIGURATION_MENU = {
16
+ damping: 39,
17
+ mass: 1.09,
18
+ stiffness: 500,
19
+ restDisplacementThreshold: 0.001,
20
+ restSpeedThreshold: 0.001,
21
+ };
22
+
23
+ enum CONTEXT_MENU_STATE {
24
+ UNDETERMINED = 0,
25
+ ACTIVE,
26
+ END,
27
+ }
28
+
29
+ const { height: WINDOW_HEIGHT, width: WINDOW_WIDTH } = Dimensions.get('screen');
30
+
31
+ const MENU_CONTAINER_WIDTH = 100;
32
+ const MENU_WIDTH = (WINDOW_WIDTH * 60) / 100;
33
+
34
+ const MENU_TRANSFORM_ORIGIN_TOLERENCE = 10;
35
+
36
+ const IS_IOS = Platform.OS === 'ios';
37
+
38
+ const FONT_SCALE = Dimensions.get('screen').fontScale;
39
+
40
+ export {
41
+ CONTEXT_MENU_STATE,
42
+ WINDOW_HEIGHT,
43
+ WINDOW_WIDTH,
44
+ MENU_WIDTH,
45
+ MENU_CONTAINER_WIDTH,
46
+ HOLD_ITEM_TRANSFORM_DURATION,
47
+ HOLD_ITEM_SCALE_DOWN_VALUE,
48
+ HOLD_ITEM_SCALE_DOWN_DURATION,
49
+ SPRING_CONFIGURATION,
50
+ SPRING_CONFIGURATION_MENU,
51
+ MENU_TRANSFORM_ORIGIN_TOLERENCE,
52
+ IS_IOS,
53
+ FONT_SCALE,
54
+ };
@@ -0,0 +1 @@
1
+ export { InternalContext } from './internal';