react-native-hold-menu-actions 0.1.11 → 0.1.13
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.
- package/lib/commonjs/components/backdrop/Backdrop.js +3 -6
- package/lib/commonjs/components/backdrop/Backdrop.js.map +1 -1
- package/lib/commonjs/components/customView/CustomView.js +142 -0
- package/lib/commonjs/components/customView/CustomView.js.map +1 -0
- package/lib/commonjs/components/holdItem/HoldItem.js +19 -24
- package/lib/commonjs/components/holdItem/HoldItem.js.map +1 -1
- package/lib/commonjs/components/menu/Menu.js +1 -3
- package/lib/commonjs/components/menu/Menu.js.map +1 -1
- package/lib/commonjs/components/menu/MenuList.js +13 -3
- package/lib/commonjs/components/menu/MenuList.js.map +1 -1
- package/lib/commonjs/components/provider/Provider.js +6 -5
- package/lib/commonjs/components/provider/Provider.js.map +1 -1
- package/lib/commonjs/context/internal.js.map +1 -1
- package/lib/commonjs/index.js.map +1 -1
- package/lib/module/components/backdrop/Backdrop.js +3 -6
- package/lib/module/components/backdrop/Backdrop.js.map +1 -1
- package/lib/module/components/customView/CustomView.js +124 -0
- package/lib/module/components/customView/CustomView.js.map +1 -0
- package/lib/module/components/holdItem/HoldItem.js +20 -24
- package/lib/module/components/holdItem/HoldItem.js.map +1 -1
- package/lib/module/components/menu/Menu.js +1 -3
- package/lib/module/components/menu/Menu.js.map +1 -1
- package/lib/module/components/menu/MenuList.js +13 -5
- package/lib/module/components/menu/MenuList.js.map +1 -1
- package/lib/module/components/provider/Provider.js +6 -5
- package/lib/module/components/provider/Provider.js.map +1 -1
- package/lib/module/context/internal.js.map +1 -1
- package/lib/module/index.js.map +1 -1
- package/lib/typescript/components/customView/CustomView.d.ts +3 -0
- package/lib/typescript/components/holdItem/HoldItem.d.ts +1 -1
- package/lib/typescript/components/holdItem/types.d.ts +20 -14
- package/lib/typescript/components/menu/types.d.ts +7 -0
- package/lib/typescript/components/provider/Provider.d.ts +1 -1
- package/lib/typescript/components/provider/types.d.ts +1 -1
- package/lib/typescript/context/internal.d.ts +3 -4
- package/lib/typescript/index.d.ts +0 -2
- package/package.json +1 -1
- package/src/components/backdrop/Backdrop.tsx +2 -5
- package/src/components/customView/CustomView.tsx +164 -0
- package/src/components/holdItem/HoldItem.tsx +20 -25
- package/src/components/holdItem/types.d.ts +20 -14
- package/src/components/menu/Menu.tsx +4 -5
- package/src/components/menu/MenuList.tsx +23 -4
- package/src/components/menu/types.d.ts +7 -0
- package/src/components/provider/Provider.tsx +16 -18
- package/src/components/provider/types.d.ts +1 -1
- package/src/context/internal.ts +3 -8
- package/src/index.ts +0 -6
- package/lib/commonjs/components/provider/Content.js +0 -74
- package/lib/commonjs/components/provider/Content.js.map +0 -1
- package/lib/module/components/provider/Content.js +0 -59
- package/lib/module/components/provider/Content.js.map +0 -1
- package/lib/typescript/components/provider/Content.d.ts +0 -2
- package/src/components/provider/Content.tsx +0 -93
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"sources":["MenuList.tsx"],"names":["React","StyleSheet","Animated","runOnJS","useAnimatedReaction","useAnimatedStyle","useDerivedValue","useSharedValue","withSpring","withTiming","calculateMenuHeight","menuAnimationAnchor","MenuItems","SPRING_CONFIGURATION_MENU","HOLD_ITEM_TRANSFORM_DURATION","CONTEXT_MENU_STATE","styles","useInternal","deepEqual","leftOrRight","MenuListComponent","state","theme","menuProps","itemList","setItemList","useState","menuHeight","itemsWithSeparator","value","items","filter","item","withSeparator","length","prevList","messageStyles","translate","anchorPosition","itemWidth","_leftPosition","menuScaleAnimation","ACTIVE","duration","opacityAnimation","left","height","opacity","transform","translateX","beginningTransformations","translateY","scale","endingTransformations","animatedInnerContainerStyle","
|
|
1
|
+
{"version":3,"sources":["MenuList.tsx"],"names":["React","StyleSheet","Animated","runOnJS","useAnimatedProps","useAnimatedReaction","useAnimatedStyle","useDerivedValue","useSharedValue","withSpring","withTiming","calculateMenuHeight","menuAnimationAnchor","BlurView","MenuItems","SPRING_CONFIGURATION_MENU","HOLD_ITEM_TRANSFORM_DURATION","IS_IOS","CONTEXT_MENU_STATE","styles","useInternal","deepEqual","leftOrRight","AnimatedView","createAnimatedComponent","MenuListComponent","state","theme","menuProps","itemList","setItemList","useState","menuHeight","itemsWithSeparator","value","items","filter","item","withSeparator","length","prevList","messageStyles","translate","anchorPosition","itemWidth","_leftPosition","menuScaleAnimation","ACTIVE","duration","opacityAnimation","left","height","opacity","transform","translateX","beginningTransformations","translateY","scale","endingTransformations","animatedInnerContainerStyle","backgroundColor","animatedProps","blurType","setter","_items","menuContainer","absoluteFillObject","menuInnerContainer","MenuList","memo"],"mappings":"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","sourcesContent":["import React from 'react';\nimport { StyleSheet } from 'react-native';\n\nimport Animated, {\n runOnJS,\n useAnimatedProps,\n useAnimatedReaction,\n useAnimatedStyle,\n useDerivedValue,\n useSharedValue,\n withSpring,\n withTiming,\n} from 'react-native-reanimated';\n\nimport {\n calculateMenuHeight,\n menuAnimationAnchor,\n} from '../../utils/calculations';\nimport { BlurView } from '@react-native-community/blur';\n\nimport MenuItems from './MenuItems';\n\nimport {\n SPRING_CONFIGURATION_MENU,\n HOLD_ITEM_TRANSFORM_DURATION,\n IS_IOS,\n CONTEXT_MENU_STATE,\n} from '../../constants';\n\nimport styles from './styles';\nimport { MenuItemProps } from './types';\nimport { useInternal } from '../../hooks';\nimport { deepEqual } from '../../utils/validations';\nimport { leftOrRight } from './calculations';\n\nconst AnimatedView = Animated.createAnimatedComponent(BlurView);\n\nconst MenuListComponent = () => {\n const { state, theme, menuProps } = useInternal();\n\n const [itemList, setItemList] = React.useState<MenuItemProps[]>([]);\n\n const menuHeight = useDerivedValue(() => {\n const itemsWithSeparator = menuProps.value.items.filter(\n item => item.withSeparator\n );\n return calculateMenuHeight(\n menuProps.value.items.length,\n itemsWithSeparator.length\n );\n }, [menuProps]);\n const prevList = useSharedValue<MenuItemProps[]>([]);\n\n const messageStyles = useAnimatedStyle(() => {\n const itemsWithSeparator = menuProps.value.items.filter(\n item => item.withSeparator\n );\n\n const translate = menuAnimationAnchor(\n menuProps.value.anchorPosition,\n menuProps.value.itemWidth,\n menuProps.value.items.length,\n itemsWithSeparator.length\n );\n\n const _leftPosition = leftOrRight(menuProps);\n\n const menuScaleAnimation = () =>\n state.value === CONTEXT_MENU_STATE.ACTIVE\n ? withSpring(1, SPRING_CONFIGURATION_MENU)\n : withTiming(0, {\n duration: HOLD_ITEM_TRANSFORM_DURATION,\n });\n\n const opacityAnimation = () =>\n withTiming(state.value === CONTEXT_MENU_STATE.ACTIVE ? 1 : 0, {\n duration: HOLD_ITEM_TRANSFORM_DURATION,\n });\n\n return {\n left: _leftPosition,\n height: menuHeight.value,\n opacity: opacityAnimation(),\n transform: [\n { translateX: translate.beginningTransformations.translateX },\n { translateY: translate.beginningTransformations.translateY },\n {\n scale: menuScaleAnimation(),\n },\n { translateX: translate.endingTransformations.translateX },\n { translateY: translate.endingTransformations.translateY },\n ],\n };\n });\n\n const animatedInnerContainerStyle = useAnimatedStyle(() => {\n return {\n backgroundColor:\n theme.value === 'light'\n ? IS_IOS\n ? 'rgba(255, 255, 255, .75)'\n : 'rgba(255, 255, 255, .95)'\n : IS_IOS\n ? 'rgba(0,0,0,0.5)'\n : 'rgba(39, 39, 39, .8)',\n };\n }, [theme]);\n\n const animatedProps = useAnimatedProps(() => {\n return { blurType: theme.value };\n }, [theme]);\n\n const setter = (items: MenuItemProps[]) => {\n setItemList(items);\n prevList.value = items;\n };\n\n useAnimatedReaction(\n () => menuProps.value.items,\n _items => {\n if (!deepEqual(_items, prevList.value)) {\n runOnJS(setter)(_items);\n }\n },\n [menuProps]\n );\n\n return (\n <AnimatedView\n blurAmount={100}\n animatedProps={animatedProps}\n style={[styles.menuContainer, messageStyles]}\n >\n <Animated.View\n style={[\n StyleSheet.absoluteFillObject,\n styles.menuInnerContainer,\n animatedInnerContainerStyle,\n ]}\n >\n <MenuItems items={itemList} />\n </Animated.View>\n </AnimatedView>\n );\n};\n\nconst MenuList = React.memo(MenuListComponent);\n\nexport default MenuList;\n"]}
|
|
@@ -8,7 +8,7 @@ import { Backdrop } from '../backdrop'; // Utils
|
|
|
8
8
|
import { InternalContext } from '../../context/internal';
|
|
9
9
|
import { CONTEXT_MENU_STATE } from '../../constants';
|
|
10
10
|
import Menu from '../menu';
|
|
11
|
-
import
|
|
11
|
+
import CustomView from '../customView/CustomView';
|
|
12
12
|
export let AnimatedIcon;
|
|
13
13
|
|
|
14
14
|
const ProviderComponent = ({
|
|
@@ -31,9 +31,10 @@ const ProviderComponent = ({
|
|
|
31
31
|
anchorPosition: 'top-center',
|
|
32
32
|
menuHeight: 0,
|
|
33
33
|
transformValue: 0,
|
|
34
|
-
actionParams: {}
|
|
34
|
+
actionParams: {},
|
|
35
|
+
hasCustomView: false
|
|
35
36
|
});
|
|
36
|
-
const
|
|
37
|
+
const customViewRef = useRef(null);
|
|
37
38
|
useEffect(() => {
|
|
38
39
|
theme.value = selectedTheme || 'light'; // eslint-disable-next-line react-hooks/exhaustive-deps
|
|
39
40
|
}, [selectedTheme]);
|
|
@@ -56,7 +57,7 @@ const ProviderComponent = ({
|
|
|
56
57
|
state,
|
|
57
58
|
theme,
|
|
58
59
|
menuProps,
|
|
59
|
-
|
|
60
|
+
customViewRef,
|
|
60
61
|
safeAreaInsets: safeAreaInsets || {
|
|
61
62
|
top: 0,
|
|
62
63
|
bottom: 0,
|
|
@@ -70,7 +71,7 @@ const ProviderComponent = ({
|
|
|
70
71
|
}
|
|
71
72
|
}, /*#__PURE__*/React.createElement(InternalContext.Provider, {
|
|
72
73
|
value: internalContextVariables
|
|
73
|
-
}, /*#__PURE__*/React.createElement(PortalProvider, null, children, /*#__PURE__*/React.createElement(Backdrop, null), /*#__PURE__*/React.createElement(Menu, null), /*#__PURE__*/React.createElement(
|
|
74
|
+
}, /*#__PURE__*/React.createElement(PortalProvider, null, children, /*#__PURE__*/React.createElement(Backdrop, null), /*#__PURE__*/React.createElement(Menu, null), /*#__PURE__*/React.createElement(CustomView, null))));
|
|
74
75
|
};
|
|
75
76
|
|
|
76
77
|
const Provider = /*#__PURE__*/memo(ProviderComponent);
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"sources":["Provider.tsx"],"names":["React","memo","useEffect","useMemo","useRef","PortalProvider","Animated","useSharedValue","useAnimatedReaction","runOnJS","GestureHandlerRootView","Backdrop","InternalContext","CONTEXT_MENU_STATE","Menu","
|
|
1
|
+
{"version":3,"sources":["Provider.tsx"],"names":["React","memo","useEffect","useMemo","useRef","PortalProvider","Animated","useSharedValue","useAnimatedReaction","runOnJS","GestureHandlerRootView","Backdrop","InternalContext","CONTEXT_MENU_STATE","Menu","CustomView","AnimatedIcon","ProviderComponent","children","theme","selectedTheme","iconComponent","safeAreaInsets","onOpen","onClose","createAnimatedComponent","state","UNDETERMINED","menuProps","itemHeight","itemWidth","itemX","itemY","items","anchorPosition","menuHeight","transformValue","actionParams","hasCustomView","customViewRef","value","ACTIVE","END","internalContextVariables","top","bottom","left","right","flex","Provider"],"mappings":"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","sourcesContent":["import React, { memo, useEffect, useMemo, useRef } from 'react';\nimport { PortalProvider } from '@gorhom/portal';\nimport Animated, { useSharedValue, useAnimatedReaction, runOnJS } from 'react-native-reanimated';\nimport { GestureHandlerRootView } from 'react-native-gesture-handler';\n\n// Components\nimport { Backdrop } from '../backdrop';\n\n// Utils\nimport { InternalContext } from '../../context/internal';\nimport { HoldMenuProviderProps } from './types';\nimport { StateProps, Action } from './reducer';\nimport { CONTEXT_MENU_STATE } from '../../constants';\nimport { MenuInternalProps } from '../menu/types';\nimport Menu from '../menu';\nimport CustomView from '../customView/CustomView';\nimport { RenderCustomView } from '../menu/types';\n\nexport interface Store {\n state: StateProps;\n dispatch?: React.Dispatch<Action>;\n}\n\nexport let AnimatedIcon: any;\n\nconst ProviderComponent = ({\n children,\n theme: selectedTheme,\n iconComponent,\n safeAreaInsets,\n onOpen,\n onClose,\n}: HoldMenuProviderProps) => {\n if (iconComponent)\n AnimatedIcon = Animated.createAnimatedComponent(iconComponent);\n\n const state = useSharedValue<CONTEXT_MENU_STATE>(\n CONTEXT_MENU_STATE.UNDETERMINED\n );\n const theme = useSharedValue<'light' | 'dark'>(selectedTheme || 'light');\n const menuProps = useSharedValue<MenuInternalProps>({\n itemHeight: 0,\n itemWidth: 0,\n itemX: 0,\n itemY: 0,\n items: [],\n anchorPosition: 'top-center',\n menuHeight: 0,\n transformValue: 0,\n actionParams: {},\n hasCustomView: false,\n });\n const customViewRef = useRef<RenderCustomView | null>(null);\n\n useEffect(() => {\n theme.value = selectedTheme || 'light';\n // eslint-disable-next-line react-hooks/exhaustive-deps\n }, [selectedTheme]);\n\n useAnimatedReaction(\n () => state.value,\n state => {\n switch (state) {\n case CONTEXT_MENU_STATE.ACTIVE: {\n if (onOpen)\n runOnJS(onOpen)();\n break\n }\n case CONTEXT_MENU_STATE.END: {\n if (onClose)\n runOnJS(onClose)();\n break\n }\n }\n },\n [state]\n );\n\n const internalContextVariables = useMemo(\n () => ({\n state,\n theme,\n menuProps,\n customViewRef,\n safeAreaInsets: safeAreaInsets || {\n top: 0,\n bottom: 0,\n left: 0,\n right: 0,\n },\n }),\n [state, theme, menuProps, safeAreaInsets]\n );\n\n return (\n <GestureHandlerRootView style={{ flex: 1 }}>\n <InternalContext.Provider value={internalContextVariables}>\n <PortalProvider>\n {children}\n <Backdrop />\n <Menu />\n <CustomView />\n </PortalProvider>\n </InternalContext.Provider>\n </GestureHandlerRootView>\n );\n};\n\nconst Provider = memo(ProviderComponent);\n\nexport default Provider;\n"]}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"sources":["internal.ts"],"names":["createContext","InternalContext"],"mappings":"AAAA,SAASA,aAAT,
|
|
1
|
+
{"version":3,"sources":["internal.ts"],"names":["createContext","InternalContext"],"mappings":"AAAA,SAASA,aAAT,QAAgD,OAAhD;AAkBA;AACA,OAAO,MAAMC,eAAe,gBAAGD,aAAa,EAArC","sourcesContent":["import { createContext, MutableRefObject } from 'react';\nimport type Animated from 'react-native-reanimated';\nimport type { CONTEXT_MENU_STATE } from '../constants';\nimport { MenuInternalProps, RenderCustomView } from '../components/menu/types';\n\nexport type InternalContextType = {\n state: Animated.SharedValue<CONTEXT_MENU_STATE>;\n theme: Animated.SharedValue<'light' | 'dark'>;\n menuProps: Animated.SharedValue<MenuInternalProps>;\n customViewRef: MutableRefObject<RenderCustomView | null>;\n safeAreaInsets?: {\n top: number;\n right: number;\n bottom: number;\n left: number;\n };\n};\n\n// @ts-ignore\nexport const InternalContext = createContext<InternalContextType>();\n"]}
|
package/lib/module/index.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"sources":["index.ts"],"names":["default","HoldItem","HoldMenuProvider","HoldMenuFlatList","HoldMenuIcon"],"mappings":"AAAA,SAASA,OAAO,IAAIC,QAApB,QAAoC,uBAApC;AACA,SAASD,OAAO,IAAIE,gBAApB,QAA4C,uBAA5C;AACA,SAASF,OAAO,IAAIG,gBAApB,QAA4C,uBAA5C;AACA,SAASH,OAAO,IAAII,YAApB,QAAwC,mBAAxC","sourcesContent":["export { default as HoldItem } from './components/holdItem';\nexport { default as HoldMenuProvider } from './components/provider';\nexport { default as HoldMenuFlatList } from './components/flatList';\nexport { default as HoldMenuIcon } from './components/icon';\n
|
|
1
|
+
{"version":3,"sources":["index.ts"],"names":["default","HoldItem","HoldMenuProvider","HoldMenuFlatList","HoldMenuIcon"],"mappings":"AAAA,SAASA,OAAO,IAAIC,QAApB,QAAoC,uBAApC;AACA,SAASD,OAAO,IAAIE,gBAApB,QAA4C,uBAA5C;AACA,SAASF,OAAO,IAAIG,gBAApB,QAA4C,uBAA5C;AACA,SAASH,OAAO,IAAII,YAApB,QAAwC,mBAAxC","sourcesContent":["export { default as HoldItem } from './components/holdItem';\nexport { default as HoldMenuProvider } from './components/provider';\nexport { default as HoldMenuFlatList } from './components/flatList';\nexport { default as HoldMenuIcon } from './components/icon';\n"]}
|
|
@@ -1,4 +1,4 @@
|
|
|
1
1
|
import React from 'react';
|
|
2
2
|
import type { HoldItemProps } from './types';
|
|
3
|
-
declare const HoldItem: React.
|
|
3
|
+
declare const HoldItem: React.MemoExoticComponent<({ items, renderCustomView, bottom, containerStyles, disableMove, menuAnchorPosition, activateOn, hapticFeedback, actionParams, closeOnTap, longPressMinDurationMs, children, }: HoldItemProps) => JSX.Element>;
|
|
4
4
|
export default HoldItem;
|
|
@@ -1,12 +1,8 @@
|
|
|
1
1
|
import { ViewStyle } from 'react-native';
|
|
2
|
-
import { MenuItemProps } from '../menu/types';
|
|
2
|
+
import { MenuItemProps, RenderCustomView } from '../menu/types';
|
|
3
3
|
import { TransformOriginAnchorPosition } from '../../utils/calculations';
|
|
4
4
|
|
|
5
|
-
|
|
6
|
-
closeMenu: () => void;
|
|
7
|
-
}
|
|
8
|
-
|
|
9
|
-
export type HoldItemProps = {
|
|
5
|
+
type HoldItemBaseProps = {
|
|
10
6
|
/**
|
|
11
7
|
* List of context menu items.
|
|
12
8
|
* @type MenuItemProps[]
|
|
@@ -14,6 +10,15 @@ export type HoldItemProps = {
|
|
|
14
10
|
*/
|
|
15
11
|
items?: MenuItemProps[];
|
|
16
12
|
|
|
13
|
+
/**
|
|
14
|
+
* Render function for a custom view displayed on the opposite side of the menu.
|
|
15
|
+
* Receives { closeMenu } prop to programmatically close the context menu.
|
|
16
|
+
* @type RenderCustomView
|
|
17
|
+
* @examples
|
|
18
|
+
* renderCustomView={({ closeMenu }) => <ReactionsBar onSelect={() => closeMenu()} />}
|
|
19
|
+
*/
|
|
20
|
+
renderCustomView?: RenderCustomView;
|
|
21
|
+
|
|
17
22
|
/**
|
|
18
23
|
* Object of keys that same name with items to match parameters to onPress actions.
|
|
19
24
|
* @type { [name: string]: (string | number)[] }
|
|
@@ -128,17 +133,18 @@ export type HoldItemProps = {
|
|
|
128
133
|
* longPressMinDurationMs={250}
|
|
129
134
|
*/
|
|
130
135
|
longPressMinDurationMs?: number;
|
|
136
|
+
};
|
|
131
137
|
|
|
132
|
-
|
|
133
|
-
|
|
134
|
-
* Allows to render custom content (e.g. reactions) above/below the menu.
|
|
135
|
-
* @type (params: RenderContentParams) => React.ReactNode
|
|
136
|
-
* @examples
|
|
137
|
-
* renderContent={({ closeMenu }) => <MyReactions onClose={closeMenu} />}
|
|
138
|
-
*/
|
|
139
|
-
renderContent?: (params: RenderContentParams) => React.ReactNode;
|
|
138
|
+
export type HoldItemWithItems = HoldItemBaseProps & {
|
|
139
|
+
items: MenuItemProps[];
|
|
140
140
|
};
|
|
141
141
|
|
|
142
|
+
export type HoldItemWithCustomView = HoldItemBaseProps & {
|
|
143
|
+
renderCustomView: RenderCustomView;
|
|
144
|
+
};
|
|
145
|
+
|
|
146
|
+
export type HoldItemProps = HoldItemWithItems | HoldItemWithCustomView;
|
|
147
|
+
|
|
142
148
|
export type GestureHandlerProps = {
|
|
143
149
|
children: React.ReactElement | React.ReactElement[];
|
|
144
150
|
};
|
|
@@ -13,6 +13,12 @@ export type MenuListProps = {
|
|
|
13
13
|
items: MenuItemProps[];
|
|
14
14
|
};
|
|
15
15
|
|
|
16
|
+
export type CustomViewProps = {
|
|
17
|
+
closeMenu: () => void;
|
|
18
|
+
};
|
|
19
|
+
|
|
20
|
+
export type RenderCustomView = (props: CustomViewProps) => React.ReactElement;
|
|
21
|
+
|
|
16
22
|
export type MenuInternalProps = {
|
|
17
23
|
items: MenuItemProps[];
|
|
18
24
|
itemHeight: number;
|
|
@@ -25,4 +31,5 @@ export type MenuInternalProps = {
|
|
|
25
31
|
actionParams: {
|
|
26
32
|
[name: string]: (string | number)[];
|
|
27
33
|
};
|
|
34
|
+
hasCustomView: boolean;
|
|
28
35
|
};
|
|
@@ -6,5 +6,5 @@ export interface Store {
|
|
|
6
6
|
dispatch?: React.Dispatch<Action>;
|
|
7
7
|
}
|
|
8
8
|
export declare let AnimatedIcon: any;
|
|
9
|
-
declare const Provider: React.
|
|
9
|
+
declare const Provider: React.MemoExoticComponent<({ children, theme: selectedTheme, iconComponent, safeAreaInsets, onOpen, onClose, }: HoldMenuProviderProps) => JSX.Element>;
|
|
10
10
|
export default Provider;
|
|
@@ -1,13 +1,12 @@
|
|
|
1
|
-
import
|
|
1
|
+
import { MutableRefObject } from 'react';
|
|
2
2
|
import type Animated from 'react-native-reanimated';
|
|
3
3
|
import type { CONTEXT_MENU_STATE } from '../constants';
|
|
4
|
-
import { MenuInternalProps } from '../components/menu/types';
|
|
5
|
-
export declare type ContentRendererRef = MutableRefObject<((params: any) => React.ReactNode) | null>;
|
|
4
|
+
import { MenuInternalProps, RenderCustomView } from '../components/menu/types';
|
|
6
5
|
export declare type InternalContextType = {
|
|
7
6
|
state: Animated.SharedValue<CONTEXT_MENU_STATE>;
|
|
8
7
|
theme: Animated.SharedValue<'light' | 'dark'>;
|
|
9
8
|
menuProps: Animated.SharedValue<MenuInternalProps>;
|
|
10
|
-
|
|
9
|
+
customViewRef: MutableRefObject<RenderCustomView | null>;
|
|
11
10
|
safeAreaInsets?: {
|
|
12
11
|
top: number;
|
|
13
12
|
right: number;
|
|
@@ -2,5 +2,3 @@ export { default as HoldItem } from './components/holdItem';
|
|
|
2
2
|
export { default as HoldMenuProvider } from './components/provider';
|
|
3
3
|
export { default as HoldMenuFlatList } from './components/flatList';
|
|
4
4
|
export { default as HoldMenuIcon } from './components/icon';
|
|
5
|
-
export type { HoldMenuProviderProps } from './components/provider';
|
|
6
|
-
export type { HoldItemProps, RenderContentParams, } from './components/holdItem/types';
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "react-native-hold-menu-actions",
|
|
3
|
-
"version": "0.1.
|
|
3
|
+
"version": "0.1.13",
|
|
4
4
|
"description": "A performant, easy to use hold to open context menu for React Native powered by Reanimated.",
|
|
5
5
|
"main": "lib/commonjs/index.js",
|
|
6
6
|
"module": "lib/module/index.js",
|
|
@@ -71,10 +71,8 @@ const BackdropComponent = () => {
|
|
|
71
71
|
);
|
|
72
72
|
|
|
73
73
|
const animatedContainerStyle = useAnimatedStyle(() => {
|
|
74
|
-
const isActive = state.value === CONTEXT_MENU_STATE.ACTIVE;
|
|
75
|
-
|
|
76
74
|
const topValueAnimation = () =>
|
|
77
|
-
|
|
75
|
+
state.value === CONTEXT_MENU_STATE.ACTIVE
|
|
78
76
|
? 0
|
|
79
77
|
: withDelay(
|
|
80
78
|
HOLD_ITEM_TRANSFORM_DURATION,
|
|
@@ -84,14 +82,13 @@ const BackdropComponent = () => {
|
|
|
84
82
|
);
|
|
85
83
|
|
|
86
84
|
const opacityValueAnimation = () =>
|
|
87
|
-
withTiming(
|
|
85
|
+
withTiming(state.value === CONTEXT_MENU_STATE.ACTIVE ? 1 : 0, {
|
|
88
86
|
duration: HOLD_ITEM_TRANSFORM_DURATION,
|
|
89
87
|
});
|
|
90
88
|
|
|
91
89
|
return {
|
|
92
90
|
top: topValueAnimation(),
|
|
93
91
|
opacity: opacityValueAnimation(),
|
|
94
|
-
pointerEvents: isActive ? 'auto' : 'none',
|
|
95
92
|
};
|
|
96
93
|
});
|
|
97
94
|
|
|
@@ -0,0 +1,164 @@
|
|
|
1
|
+
import React, { useCallback, useState } from 'react';
|
|
2
|
+
import { LayoutChangeEvent, StyleSheet } from 'react-native';
|
|
3
|
+
|
|
4
|
+
import Animated, {
|
|
5
|
+
runOnJS,
|
|
6
|
+
useAnimatedReaction,
|
|
7
|
+
useAnimatedStyle,
|
|
8
|
+
withSpring,
|
|
9
|
+
withTiming,
|
|
10
|
+
} from 'react-native-reanimated';
|
|
11
|
+
|
|
12
|
+
import { useInternal } from '../../hooks';
|
|
13
|
+
import {
|
|
14
|
+
HOLD_ITEM_TRANSFORM_DURATION,
|
|
15
|
+
CONTEXT_MENU_STATE,
|
|
16
|
+
SPRING_CONFIGURATION,
|
|
17
|
+
SPRING_CONFIGURATION_MENU,
|
|
18
|
+
WINDOW_WIDTH,
|
|
19
|
+
} from '../../constants';
|
|
20
|
+
import { RenderCustomView } from '../menu/types';
|
|
21
|
+
|
|
22
|
+
const CustomViewComponent = () => {
|
|
23
|
+
const { state, menuProps, customViewRef } = useInternal();
|
|
24
|
+
|
|
25
|
+
const [renderFn, setRenderFn] = useState<RenderCustomView | null>(null);
|
|
26
|
+
const [customViewHeight, setCustomViewHeight] = useState(0);
|
|
27
|
+
const [customViewWidth, setCustomViewWidth] = useState(0);
|
|
28
|
+
|
|
29
|
+
const closeMenu = useCallback(() => {
|
|
30
|
+
state.value = CONTEXT_MENU_STATE.END;
|
|
31
|
+
}, [state]);
|
|
32
|
+
|
|
33
|
+
const onLayout = useCallback((event: LayoutChangeEvent) => {
|
|
34
|
+
const { height, width } = event.nativeEvent.layout;
|
|
35
|
+
setCustomViewHeight(height);
|
|
36
|
+
setCustomViewWidth(width);
|
|
37
|
+
}, []);
|
|
38
|
+
|
|
39
|
+
const clearRenderFn = useCallback(() => {
|
|
40
|
+
setRenderFn(null);
|
|
41
|
+
setCustomViewHeight(0);
|
|
42
|
+
setCustomViewWidth(0);
|
|
43
|
+
}, []);
|
|
44
|
+
|
|
45
|
+
const updateRenderFn = useCallback(
|
|
46
|
+
(hasCustomView: boolean) => {
|
|
47
|
+
if (hasCustomView && customViewRef.current) {
|
|
48
|
+
setRenderFn(() => customViewRef.current);
|
|
49
|
+
} else {
|
|
50
|
+
setTimeout(clearRenderFn, HOLD_ITEM_TRANSFORM_DURATION);
|
|
51
|
+
}
|
|
52
|
+
},
|
|
53
|
+
[customViewRef, clearRenderFn]
|
|
54
|
+
);
|
|
55
|
+
|
|
56
|
+
useAnimatedReaction(
|
|
57
|
+
() => ({
|
|
58
|
+
currentState: state.value,
|
|
59
|
+
hasCustomView: menuProps.value.hasCustomView,
|
|
60
|
+
}),
|
|
61
|
+
({ currentState, hasCustomView }) => {
|
|
62
|
+
if (currentState === CONTEXT_MENU_STATE.ACTIVE && hasCustomView) {
|
|
63
|
+
runOnJS(updateRenderFn)(true);
|
|
64
|
+
} else if (currentState === CONTEXT_MENU_STATE.END) {
|
|
65
|
+
runOnJS(updateRenderFn)(false);
|
|
66
|
+
}
|
|
67
|
+
},
|
|
68
|
+
[state, menuProps]
|
|
69
|
+
);
|
|
70
|
+
|
|
71
|
+
const wrapperStyles = useAnimatedStyle(() => {
|
|
72
|
+
const anchorPositionVertical = menuProps.value.anchorPosition.split('-')[0];
|
|
73
|
+
const isAbove = anchorPositionVertical === 'top';
|
|
74
|
+
|
|
75
|
+
// Fixed anchor point: top of the item
|
|
76
|
+
const top = menuProps.value.itemY;
|
|
77
|
+
|
|
78
|
+
const SCREEN_PADDING = 16;
|
|
79
|
+
const MAX_WIDTH = WINDOW_WIDTH - SCREEN_PADDING * 2;
|
|
80
|
+
|
|
81
|
+
// Start at item's left edge
|
|
82
|
+
let left = menuProps.value.itemX;
|
|
83
|
+
|
|
84
|
+
// If the view overflows the right edge, shift left
|
|
85
|
+
if (
|
|
86
|
+
customViewWidth > 0 &&
|
|
87
|
+
left + customViewWidth > WINDOW_WIDTH - SCREEN_PADDING
|
|
88
|
+
) {
|
|
89
|
+
left = Math.max(
|
|
90
|
+
SCREEN_PADDING,
|
|
91
|
+
WINDOW_WIDTH - customViewWidth - SCREEN_PADDING
|
|
92
|
+
);
|
|
93
|
+
}
|
|
94
|
+
if (left < SCREEN_PADDING) {
|
|
95
|
+
left = SCREEN_PADDING;
|
|
96
|
+
}
|
|
97
|
+
const tY = menuProps.value.transformValue;
|
|
98
|
+
|
|
99
|
+
// Positional offset via translateY:
|
|
100
|
+
// above item: shift up by customViewHeight + gap
|
|
101
|
+
// below item: shift down by itemHeight + gap
|
|
102
|
+
const positionOffsetY = isAbove
|
|
103
|
+
? -(customViewHeight + 8)
|
|
104
|
+
: menuProps.value.itemHeight + 8;
|
|
105
|
+
|
|
106
|
+
const scaleAnimation =
|
|
107
|
+
state.value === CONTEXT_MENU_STATE.ACTIVE
|
|
108
|
+
? withSpring(1, SPRING_CONFIGURATION_MENU)
|
|
109
|
+
: withTiming(0, { duration: HOLD_ITEM_TRANSFORM_DURATION });
|
|
110
|
+
|
|
111
|
+
const opacityAnimation = withTiming(
|
|
112
|
+
state.value === CONTEXT_MENU_STATE.ACTIVE ? 1 : 0,
|
|
113
|
+
{ duration: HOLD_ITEM_TRANSFORM_DURATION }
|
|
114
|
+
);
|
|
115
|
+
|
|
116
|
+
// Scale anchor: scale from the edge closest to the item
|
|
117
|
+
const scaleAnchorOffset = isAbove
|
|
118
|
+
? customViewHeight / 2
|
|
119
|
+
: -(customViewHeight / 2);
|
|
120
|
+
|
|
121
|
+
return {
|
|
122
|
+
top,
|
|
123
|
+
left,
|
|
124
|
+
maxWidth: MAX_WIDTH,
|
|
125
|
+
opacity: opacityAnimation,
|
|
126
|
+
transform: [
|
|
127
|
+
// 1. Transform value (screen boundary compensation)
|
|
128
|
+
{
|
|
129
|
+
translateY:
|
|
130
|
+
state.value === CONTEXT_MENU_STATE.ACTIVE
|
|
131
|
+
? withSpring(tY, SPRING_CONFIGURATION)
|
|
132
|
+
: withTiming(0, { duration: HOLD_ITEM_TRANSFORM_DURATION }),
|
|
133
|
+
},
|
|
134
|
+
// 2. Position offset (animated when customViewHeight changes)
|
|
135
|
+
{ translateY: withSpring(positionOffsetY, SPRING_CONFIGURATION_MENU) },
|
|
136
|
+
// 3. Scale anchor: move to edge -> scale -> move back
|
|
137
|
+
{ translateY: scaleAnchorOffset },
|
|
138
|
+
{ scale: scaleAnimation },
|
|
139
|
+
{ translateY: -scaleAnchorOffset },
|
|
140
|
+
],
|
|
141
|
+
};
|
|
142
|
+
}, [menuProps, customViewHeight, customViewWidth]);
|
|
143
|
+
|
|
144
|
+
if (!renderFn) return null;
|
|
145
|
+
|
|
146
|
+
return (
|
|
147
|
+
<Animated.View style={[styles.customViewWrapper, wrapperStyles]}>
|
|
148
|
+
<Animated.View onLayout={onLayout}>
|
|
149
|
+
{renderFn({ closeMenu })}
|
|
150
|
+
</Animated.View>
|
|
151
|
+
</Animated.View>
|
|
152
|
+
);
|
|
153
|
+
};
|
|
154
|
+
|
|
155
|
+
const styles = StyleSheet.create({
|
|
156
|
+
customViewWrapper: {
|
|
157
|
+
position: 'absolute',
|
|
158
|
+
zIndex: 20,
|
|
159
|
+
},
|
|
160
|
+
});
|
|
161
|
+
|
|
162
|
+
const CustomView = React.memo(CustomViewComponent);
|
|
163
|
+
|
|
164
|
+
export default CustomView;
|
|
@@ -51,13 +51,13 @@ import styles from './styles';
|
|
|
51
51
|
import type { HoldItemProps, GestureHandlerProps } from './types';
|
|
52
52
|
import styleGuide from '../../styleGuide';
|
|
53
53
|
import { useInternal } from '../../hooks';
|
|
54
|
-
import { MenuItemProps } from '../menu/types';
|
|
55
54
|
//#endregion
|
|
56
55
|
|
|
57
56
|
type Context = { didMeasureLayout: boolean };
|
|
58
|
-
|
|
59
|
-
const HoldItemComponent
|
|
60
|
-
items
|
|
57
|
+
|
|
58
|
+
const HoldItemComponent = ({
|
|
59
|
+
items,
|
|
60
|
+
renderCustomView,
|
|
61
61
|
bottom,
|
|
62
62
|
containerStyles,
|
|
63
63
|
disableMove,
|
|
@@ -68,10 +68,9 @@ const HoldItemComponent: React.FC<HoldItemProps> = ({
|
|
|
68
68
|
closeOnTap,
|
|
69
69
|
longPressMinDurationMs = 150,
|
|
70
70
|
children,
|
|
71
|
-
|
|
72
|
-
}) => {
|
|
71
|
+
}: HoldItemProps) => {
|
|
73
72
|
//#region hooks
|
|
74
|
-
const { state, menuProps,
|
|
73
|
+
const { state, menuProps, customViewRef, safeAreaInsets } = useInternal();
|
|
75
74
|
const deviceOrientation = useDeviceOrientation();
|
|
76
75
|
//#endregion
|
|
77
76
|
|
|
@@ -92,6 +91,7 @@ const HoldItemComponent: React.FC<HoldItemProps> = ({
|
|
|
92
91
|
|
|
93
92
|
const key = useMemo(() => `hold-item-${nanoid()}`, []);
|
|
94
93
|
const menuHeight = useMemo(() => {
|
|
94
|
+
if (!items || items.length === 0) return 0;
|
|
95
95
|
const itemsWithSeparator = items.filter(item => item.withSeparator);
|
|
96
96
|
return calculateMenuHeight(items.length, itemsWithSeparator.length);
|
|
97
97
|
}, [items]);
|
|
@@ -154,7 +154,7 @@ const HoldItemComponent: React.FC<HoldItemProps> = ({
|
|
|
154
154
|
const height =
|
|
155
155
|
deviceOrientation === 'portrait' ? WINDOW_HEIGHT : WINDOW_WIDTH;
|
|
156
156
|
|
|
157
|
-
const isAnchorPointTop =
|
|
157
|
+
const isAnchorPointTop = transformOrigin.value.includes('top');
|
|
158
158
|
|
|
159
159
|
let tY = 0;
|
|
160
160
|
if (!disableMove) {
|
|
@@ -177,9 +177,15 @@ const HoldItemComponent: React.FC<HoldItemProps> = ({
|
|
|
177
177
|
return tY;
|
|
178
178
|
};
|
|
179
179
|
|
|
180
|
+
const updateCustomViewRef = () => {
|
|
181
|
+
customViewRef.current = renderCustomView || null;
|
|
182
|
+
};
|
|
183
|
+
|
|
180
184
|
const setMenuProps = () => {
|
|
181
185
|
'worklet';
|
|
182
186
|
|
|
187
|
+
runOnJS(updateCustomViewRef)();
|
|
188
|
+
|
|
183
189
|
menuProps.value = {
|
|
184
190
|
itemHeight: itemRectHeight.value,
|
|
185
191
|
itemWidth: itemRectWidth.value,
|
|
@@ -187,9 +193,10 @@ const HoldItemComponent: React.FC<HoldItemProps> = ({
|
|
|
187
193
|
itemX: itemRectX.value,
|
|
188
194
|
anchorPosition: transformOrigin.value,
|
|
189
195
|
menuHeight: menuHeight,
|
|
190
|
-
items,
|
|
196
|
+
items: items || [],
|
|
191
197
|
transformValue: transformValue.value,
|
|
192
198
|
actionParams: actionParams || {},
|
|
199
|
+
hasCustomView: !!renderCustomView,
|
|
193
200
|
};
|
|
194
201
|
};
|
|
195
202
|
|
|
@@ -200,32 +207,20 @@ const HoldItemComponent: React.FC<HoldItemProps> = ({
|
|
|
200
207
|
});
|
|
201
208
|
};
|
|
202
209
|
|
|
203
|
-
const setContentRenderer = () => {
|
|
204
|
-
if (renderContent) {
|
|
205
|
-
contentRenderer.current = (params: any) =>
|
|
206
|
-
renderContent({ closeMenu: params });
|
|
207
|
-
} else {
|
|
208
|
-
contentRenderer.current = null;
|
|
209
|
-
}
|
|
210
|
-
};
|
|
211
|
-
|
|
212
210
|
const onCompletion = (isFinised?: boolean) => {
|
|
213
211
|
'worklet';
|
|
214
|
-
const
|
|
215
|
-
|
|
216
|
-
if (isFinised &&
|
|
212
|
+
const hasItems = items && items.length > 0;
|
|
213
|
+
const hasCustomView = !!renderCustomView;
|
|
214
|
+
if (isFinised && (hasItems || hasCustomView)) {
|
|
217
215
|
state.value = CONTEXT_MENU_STATE.ACTIVE;
|
|
218
216
|
isActive.value = true;
|
|
219
217
|
scaleBack();
|
|
220
|
-
runOnJS(setContentRenderer)();
|
|
221
218
|
if (hapticFeedback !== 'None') {
|
|
222
219
|
runOnJS(hapticResponse)();
|
|
223
220
|
}
|
|
224
221
|
}
|
|
225
222
|
|
|
226
223
|
isAnimationStarted.value = false;
|
|
227
|
-
|
|
228
|
-
// TODO: Warn user if item list is empty or not given
|
|
229
224
|
};
|
|
230
225
|
|
|
231
226
|
const scaleHold = () => {
|
|
@@ -456,6 +451,6 @@ const HoldItemComponent: React.FC<HoldItemProps> = ({
|
|
|
456
451
|
//#endregion
|
|
457
452
|
};
|
|
458
453
|
|
|
459
|
-
const HoldItem = memo(HoldItemComponent)
|
|
454
|
+
const HoldItem = memo(HoldItemComponent);
|
|
460
455
|
|
|
461
456
|
export default HoldItem;
|
|
@@ -1,12 +1,8 @@
|
|
|
1
1
|
import { ViewStyle } from 'react-native';
|
|
2
|
-
import { MenuItemProps } from '../menu/types';
|
|
2
|
+
import { MenuItemProps, RenderCustomView } from '../menu/types';
|
|
3
3
|
import { TransformOriginAnchorPosition } from '../../utils/calculations';
|
|
4
4
|
|
|
5
|
-
|
|
6
|
-
closeMenu: () => void;
|
|
7
|
-
}
|
|
8
|
-
|
|
9
|
-
export type HoldItemProps = {
|
|
5
|
+
type HoldItemBaseProps = {
|
|
10
6
|
/**
|
|
11
7
|
* List of context menu items.
|
|
12
8
|
* @type MenuItemProps[]
|
|
@@ -14,6 +10,15 @@ export type HoldItemProps = {
|
|
|
14
10
|
*/
|
|
15
11
|
items?: MenuItemProps[];
|
|
16
12
|
|
|
13
|
+
/**
|
|
14
|
+
* Render function for a custom view displayed on the opposite side of the menu.
|
|
15
|
+
* Receives { closeMenu } prop to programmatically close the context menu.
|
|
16
|
+
* @type RenderCustomView
|
|
17
|
+
* @examples
|
|
18
|
+
* renderCustomView={({ closeMenu }) => <ReactionsBar onSelect={() => closeMenu()} />}
|
|
19
|
+
*/
|
|
20
|
+
renderCustomView?: RenderCustomView;
|
|
21
|
+
|
|
17
22
|
/**
|
|
18
23
|
* Object of keys that same name with items to match parameters to onPress actions.
|
|
19
24
|
* @type { [name: string]: (string | number)[] }
|
|
@@ -128,17 +133,18 @@ export type HoldItemProps = {
|
|
|
128
133
|
* longPressMinDurationMs={250}
|
|
129
134
|
*/
|
|
130
135
|
longPressMinDurationMs?: number;
|
|
136
|
+
};
|
|
131
137
|
|
|
132
|
-
|
|
133
|
-
|
|
134
|
-
* Allows to render custom content (e.g. reactions) above/below the menu.
|
|
135
|
-
* @type (params: RenderContentParams) => React.ReactNode
|
|
136
|
-
* @examples
|
|
137
|
-
* renderContent={({ closeMenu }) => <MyReactions onClose={closeMenu} />}
|
|
138
|
-
*/
|
|
139
|
-
renderContent?: (params: RenderContentParams) => React.ReactNode;
|
|
138
|
+
export type HoldItemWithItems = HoldItemBaseProps & {
|
|
139
|
+
items: MenuItemProps[];
|
|
140
140
|
};
|
|
141
141
|
|
|
142
|
+
export type HoldItemWithCustomView = HoldItemBaseProps & {
|
|
143
|
+
renderCustomView: RenderCustomView;
|
|
144
|
+
};
|
|
145
|
+
|
|
146
|
+
export type HoldItemProps = HoldItemWithItems | HoldItemWithCustomView;
|
|
147
|
+
|
|
142
148
|
export type GestureHandlerProps = {
|
|
143
149
|
children: React.ReactElement | React.ReactElement[];
|
|
144
150
|
};
|