react-native-hold-menu-actions 0.1.14 → 0.1.16

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 (39) hide show
  1. package/lib/commonjs/components/backdrop/Backdrop.js +3 -3
  2. package/lib/commonjs/components/backdrop/Backdrop.js.map +1 -1
  3. package/lib/commonjs/components/customView/CustomView.js +35 -25
  4. package/lib/commonjs/components/customView/CustomView.js.map +1 -1
  5. package/lib/commonjs/components/holdItem/HoldItem.js +3 -1
  6. package/lib/commonjs/components/holdItem/HoldItem.js.map +1 -1
  7. package/lib/commonjs/components/menu/Menu.js +5 -2
  8. package/lib/commonjs/components/menu/Menu.js.map +1 -1
  9. package/lib/commonjs/components/menu/MenuList.js +4 -13
  10. package/lib/commonjs/components/menu/MenuList.js.map +1 -1
  11. package/lib/commonjs/components/provider/Provider.js +1 -0
  12. package/lib/commonjs/components/provider/Provider.js.map +1 -1
  13. package/lib/commonjs/utils/calculations.js +48 -1
  14. package/lib/commonjs/utils/calculations.js.map +1 -1
  15. package/lib/module/components/backdrop/Backdrop.js +2 -2
  16. package/lib/module/components/backdrop/Backdrop.js.map +1 -1
  17. package/lib/module/components/customView/CustomView.js +36 -27
  18. package/lib/module/components/customView/CustomView.js.map +1 -1
  19. package/lib/module/components/holdItem/HoldItem.js +4 -2
  20. package/lib/module/components/holdItem/HoldItem.js.map +1 -1
  21. package/lib/module/components/menu/Menu.js +5 -3
  22. package/lib/module/components/menu/Menu.js.map +1 -1
  23. package/lib/module/components/menu/MenuList.js +6 -13
  24. package/lib/module/components/menu/MenuList.js.map +1 -1
  25. package/lib/module/components/provider/Provider.js +1 -0
  26. package/lib/module/components/provider/Provider.js.map +1 -1
  27. package/lib/module/utils/calculations.js +44 -0
  28. package/lib/module/utils/calculations.js.map +1 -1
  29. package/lib/typescript/components/menu/MenuList.d.ts +1 -1
  30. package/lib/typescript/components/menu/types.d.ts +1 -0
  31. package/lib/typescript/utils/calculations.d.ts +1 -0
  32. package/package.json +1 -1
  33. package/src/components/customView/CustomView.tsx +51 -36
  34. package/src/components/holdItem/HoldItem.tsx +14 -1
  35. package/src/components/menu/Menu.tsx +13 -2
  36. package/src/components/menu/MenuList.tsx +4 -23
  37. package/src/components/menu/types.d.ts +1 -0
  38. package/src/components/provider/Provider.tsx +10 -7
  39. package/src/utils/calculations.ts +53 -0
@@ -1 +1 @@
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"]}
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","color","backgroundColor","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 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';\n\nimport MenuItems from './MenuItems';\n\nimport {\n SPRING_CONFIGURATION_MENU,\n HOLD_ITEM_TRANSFORM_DURATION,\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 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 const color = theme.value === 'light' ? '#fff' : '#1A1A1A';\n return {\n backgroundColor: color,\n };\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 if (itemList.length === 0) return null;\n\n return (\n <Animated.View style={[styles.menuContainer, messageStyles]}>\n <Animated.View\n style={[\n StyleSheet.absoluteFillObject,\n styles.menuInnerContainer,\n animatedInnerContainerStyle,\n ]}\n >\n <MenuItems items={itemList} />\n </Animated.View>\n </Animated.View>\n );\n};\n\nconst MenuList = React.memo(MenuListComponent);\n\nexport default MenuList;\n"]}
@@ -31,6 +31,7 @@ const ProviderComponent = ({
31
31
  anchorPosition: 'top-center',
32
32
  menuHeight: 0,
33
33
  transformValue: 0,
34
+ customViewHeight: 0,
34
35
  actionParams: {},
35
36
  hasCustomView: false
36
37
  });
@@ -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","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
+ {"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","customViewHeight","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, {\n useSharedValue,\n useAnimatedReaction,\n runOnJS,\n} 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 customViewHeight: 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) runOnJS(onOpen)();\n break;\n }\n case CONTEXT_MENU_STATE.END: {\n if (onClose) 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"]}
@@ -32,6 +32,50 @@ export const menuAnimationAnchor = (anchorPoint, itemWidth, itemLength, itemsWit
32
32
  }
33
33
  };
34
34
  };
35
+ export const calculateDynamicTransformValue = (itemY, itemHeight, menuHeight, customViewHeight, anchorPosition, screenHeight, safeAreaTop, safeAreaBottom) => {
36
+ 'worklet';
37
+
38
+ const GAP = styleGuide.spacing;
39
+ const isAnchorTop = anchorPosition.includes('top');
40
+
41
+ if (isAnchorTop) {
42
+ // Menu below item, custom view above item
43
+ // Total space needed below item: itemHeight + gap + menuHeight + safeAreaBottom
44
+ // Total space needed above item: customViewHeight + gap + safeAreaTop
45
+ const bottomEdge = itemY + itemHeight + GAP + menuHeight + safeAreaBottom;
46
+ const topEdge = itemY - GAP - customViewHeight - safeAreaTop;
47
+ let tY = 0; // If bottom overflows screen
48
+
49
+ if (bottomEdge > screenHeight) {
50
+ tY = screenHeight - bottomEdge;
51
+ } // After shifting, check if custom view goes above safe area
52
+
53
+
54
+ if (topEdge + tY < 0) {
55
+ tY = -topEdge;
56
+ }
57
+
58
+ return tY;
59
+ } else {
60
+ // Menu above item, custom view below item
61
+ // Total space needed above item: menuHeight + gap + safeAreaTop
62
+ // Total space needed below item: itemHeight + gap + customViewHeight + safeAreaBottom
63
+ const topEdge = itemY - GAP - menuHeight - safeAreaTop;
64
+ const bottomEdge = itemY + itemHeight + GAP + customViewHeight + safeAreaBottom;
65
+ let tY = 0; // If top overflows screen (goes above safe area)
66
+
67
+ if (topEdge < 0) {
68
+ tY = -topEdge;
69
+ } // After shifting, check if custom view goes below screen
70
+
71
+
72
+ if (bottomEdge + tY > screenHeight) {
73
+ tY = screenHeight - bottomEdge;
74
+ }
75
+
76
+ return tY;
77
+ }
78
+ };
35
79
  export const getTransformOrigin = (posX, itemWidth, windowWidth, bottom) => {
36
80
  'worklet';
37
81
 
@@ -1 +1 @@
1
- {"version":3,"sources":["calculations.ts"],"names":["styleGuide","MENU_WIDTH","MENU_TRANSFORM_ORIGIN_TOLERENCE","FONT_SCALE","MenuItemHeight","typography","callout","lineHeight","spacing","calculateMenuHeight","itemLength","separatorCount","menuAnimationAnchor","anchorPoint","itemWidth","itemsWithSeparatorLength","MenuHeight","splittetAnchorName","split","Center1","Center2","TyTop1","TyTop2","TxLeft1","TxLeft2","beginningTransformations","translateX","translateY","endingTransformations","getTransformOrigin","posX","windowWidth","bottom","distanceToLeft","Math","round","distanceToRight","position","majority","abs"],"mappings":"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","sourcesContent":["import styleGuide from '../styleGuide';\nimport {\n MENU_WIDTH,\n MENU_TRANSFORM_ORIGIN_TOLERENCE,\n FONT_SCALE,\n} from '../constants';\n\nexport const MenuItemHeight = () => {\n 'worklet';\n return (\n styleGuide.typography.callout.lineHeight * FONT_SCALE +\n styleGuide.spacing * 2.5\n );\n};\n\nexport const calculateMenuHeight = (\n itemLength: number,\n separatorCount: number\n) => {\n 'worklet';\n return (\n MenuItemHeight() * itemLength +\n (itemLength - 1) +\n separatorCount * styleGuide.spacing\n );\n};\n\nexport type TransformOriginAnchorPosition =\n | 'top-right'\n | 'top-left'\n | 'top-center'\n | 'bottom-right'\n | 'bottom-left'\n | 'bottom-center';\n\nexport const menuAnimationAnchor = (\n anchorPoint: TransformOriginAnchorPosition,\n itemWidth: number,\n itemLength: number,\n itemsWithSeparatorLength: number\n) => {\n 'worklet';\n const MenuHeight = calculateMenuHeight(itemLength, itemsWithSeparatorLength);\n const splittetAnchorName: string[] = anchorPoint.split('-');\n\n const Center1 = itemWidth;\n const Center2 = 0;\n\n const TyTop1 = -(MenuHeight / 2);\n const TyTop2 = MenuHeight / 2;\n\n const TxLeft1 = (MENU_WIDTH / 2) * -1;\n const TxLeft2 = (MENU_WIDTH / 2) * 1;\n\n return {\n beginningTransformations: {\n translateX:\n splittetAnchorName[1] === 'right'\n ? -TxLeft1\n : splittetAnchorName[1] === 'left'\n ? TxLeft1\n : Center1,\n translateY:\n splittetAnchorName[0] === 'top'\n ? TyTop1\n : splittetAnchorName[0] === 'bottom'\n ? TyTop1\n : Center2,\n },\n endingTransformations: {\n translateX:\n splittetAnchorName[1] === 'right'\n ? -TxLeft2\n : splittetAnchorName[1] === 'left'\n ? TxLeft2\n : Center2,\n translateY:\n splittetAnchorName[0] === 'top'\n ? TyTop2\n : splittetAnchorName[0] === 'bottom'\n ? -TyTop2\n : Center2,\n },\n };\n};\n\nexport const getTransformOrigin = (\n posX: number,\n itemWidth: number,\n windowWidth: number,\n bottom?: boolean\n): TransformOriginAnchorPosition => {\n 'worklet';\n const distanceToLeft = Math.round(posX + itemWidth / 2);\n const distanceToRight = Math.round(windowWidth - distanceToLeft);\n\n let position: TransformOriginAnchorPosition = bottom\n ? 'bottom-right'\n : 'top-right';\n\n const majority = Math.abs(distanceToLeft - distanceToRight);\n\n if (majority < MENU_TRANSFORM_ORIGIN_TOLERENCE) {\n position = bottom ? 'bottom-center' : 'top-center';\n } else if (distanceToLeft < distanceToRight) {\n position = bottom ? 'bottom-left' : 'top-left';\n }\n\n return position;\n};\n"]}
1
+ {"version":3,"sources":["calculations.ts"],"names":["styleGuide","MENU_WIDTH","MENU_TRANSFORM_ORIGIN_TOLERENCE","FONT_SCALE","MenuItemHeight","typography","callout","lineHeight","spacing","calculateMenuHeight","itemLength","separatorCount","menuAnimationAnchor","anchorPoint","itemWidth","itemsWithSeparatorLength","MenuHeight","splittetAnchorName","split","Center1","Center2","TyTop1","TyTop2","TxLeft1","TxLeft2","beginningTransformations","translateX","translateY","endingTransformations","calculateDynamicTransformValue","itemY","itemHeight","menuHeight","customViewHeight","anchorPosition","screenHeight","safeAreaTop","safeAreaBottom","GAP","isAnchorTop","includes","bottomEdge","topEdge","tY","getTransformOrigin","posX","windowWidth","bottom","distanceToLeft","Math","round","distanceToRight","position","majority","abs"],"mappings":"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","sourcesContent":["import styleGuide from '../styleGuide';\nimport {\n MENU_WIDTH,\n MENU_TRANSFORM_ORIGIN_TOLERENCE,\n FONT_SCALE,\n} from '../constants';\n\nexport const MenuItemHeight = () => {\n 'worklet';\n return (\n styleGuide.typography.callout.lineHeight * FONT_SCALE +\n styleGuide.spacing * 2.5\n );\n};\n\nexport const calculateMenuHeight = (\n itemLength: number,\n separatorCount: number\n) => {\n 'worklet';\n return (\n MenuItemHeight() * itemLength +\n (itemLength - 1) +\n separatorCount * styleGuide.spacing\n );\n};\n\nexport type TransformOriginAnchorPosition =\n | 'top-right'\n | 'top-left'\n | 'top-center'\n | 'bottom-right'\n | 'bottom-left'\n | 'bottom-center';\n\nexport const menuAnimationAnchor = (\n anchorPoint: TransformOriginAnchorPosition,\n itemWidth: number,\n itemLength: number,\n itemsWithSeparatorLength: number\n) => {\n 'worklet';\n const MenuHeight = calculateMenuHeight(itemLength, itemsWithSeparatorLength);\n const splittetAnchorName: string[] = anchorPoint.split('-');\n\n const Center1 = itemWidth;\n const Center2 = 0;\n\n const TyTop1 = -(MenuHeight / 2);\n const TyTop2 = MenuHeight / 2;\n\n const TxLeft1 = (MENU_WIDTH / 2) * -1;\n const TxLeft2 = (MENU_WIDTH / 2) * 1;\n\n return {\n beginningTransformations: {\n translateX:\n splittetAnchorName[1] === 'right'\n ? -TxLeft1\n : splittetAnchorName[1] === 'left'\n ? TxLeft1\n : Center1,\n translateY:\n splittetAnchorName[0] === 'top'\n ? TyTop1\n : splittetAnchorName[0] === 'bottom'\n ? TyTop1\n : Center2,\n },\n endingTransformations: {\n translateX:\n splittetAnchorName[1] === 'right'\n ? -TxLeft2\n : splittetAnchorName[1] === 'left'\n ? TxLeft2\n : Center2,\n translateY:\n splittetAnchorName[0] === 'top'\n ? TyTop2\n : splittetAnchorName[0] === 'bottom'\n ? -TyTop2\n : Center2,\n },\n };\n};\n\nexport const calculateDynamicTransformValue = (\n itemY: number,\n itemHeight: number,\n menuHeight: number,\n customViewHeight: number,\n anchorPosition: TransformOriginAnchorPosition,\n screenHeight: number,\n safeAreaTop: number,\n safeAreaBottom: number\n): number => {\n 'worklet';\n const GAP = styleGuide.spacing;\n const isAnchorTop = anchorPosition.includes('top');\n\n if (isAnchorTop) {\n // Menu below item, custom view above item\n // Total space needed below item: itemHeight + gap + menuHeight + safeAreaBottom\n // Total space needed above item: customViewHeight + gap + safeAreaTop\n const bottomEdge = itemY + itemHeight + GAP + menuHeight + safeAreaBottom;\n const topEdge = itemY - GAP - customViewHeight - safeAreaTop;\n\n let tY = 0;\n\n // If bottom overflows screen\n if (bottomEdge > screenHeight) {\n tY = screenHeight - bottomEdge;\n }\n // After shifting, check if custom view goes above safe area\n if (topEdge + tY < 0) {\n tY = -topEdge;\n }\n return tY;\n } else {\n // Menu above item, custom view below item\n // Total space needed above item: menuHeight + gap + safeAreaTop\n // Total space needed below item: itemHeight + gap + customViewHeight + safeAreaBottom\n const topEdge = itemY - GAP - menuHeight - safeAreaTop;\n const bottomEdge =\n itemY + itemHeight + GAP + customViewHeight + safeAreaBottom;\n\n let tY = 0;\n // If top overflows screen (goes above safe area)\n if (topEdge < 0) {\n tY = -topEdge;\n }\n // After shifting, check if custom view goes below screen\n if (bottomEdge + tY > screenHeight) {\n tY = screenHeight - bottomEdge;\n }\n return tY;\n }\n};\n\nexport const getTransformOrigin = (\n posX: number,\n itemWidth: number,\n windowWidth: number,\n bottom?: boolean\n): TransformOriginAnchorPosition => {\n 'worklet';\n const distanceToLeft = Math.round(posX + itemWidth / 2);\n const distanceToRight = Math.round(windowWidth - distanceToLeft);\n\n let position: TransformOriginAnchorPosition = bottom\n ? 'bottom-right'\n : 'top-right';\n\n const majority = Math.abs(distanceToLeft - distanceToRight);\n\n if (majority < MENU_TRANSFORM_ORIGIN_TOLERENCE) {\n position = bottom ? 'bottom-center' : 'top-center';\n } else if (distanceToLeft < distanceToRight) {\n position = bottom ? 'bottom-left' : 'top-left';\n }\n\n return position;\n};\n"]}
@@ -1,3 +1,3 @@
1
1
  import React from 'react';
2
- declare const MenuList: React.MemoExoticComponent<() => JSX.Element>;
2
+ declare const MenuList: React.MemoExoticComponent<() => JSX.Element | null>;
3
3
  export default MenuList;
@@ -28,6 +28,7 @@ export type MenuInternalProps = {
28
28
  anchorPosition: TransformOriginAnchorPosition;
29
29
  menuHeight: number;
30
30
  transformValue: number;
31
+ customViewHeight: number;
31
32
  actionParams: {
32
33
  [name: string]: (string | number)[];
33
34
  };
@@ -11,4 +11,5 @@ export declare const menuAnimationAnchor: (anchorPoint: TransformOriginAnchorPos
11
11
  translateY: number;
12
12
  };
13
13
  };
14
+ export declare const calculateDynamicTransformValue: (itemY: number, itemHeight: number, menuHeight: number, customViewHeight: number, anchorPosition: TransformOriginAnchorPosition, screenHeight: number, safeAreaTop: number, safeAreaBottom: number) => number;
14
15
  export declare const getTransformOrigin: (posX: number, itemWidth: number, windowWidth: number, bottom?: boolean | undefined) => TransformOriginAnchorPosition;
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "react-native-hold-menu-actions",
3
- "version": "0.1.14",
3
+ "version": "0.1.16",
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",
@@ -5,6 +5,7 @@ import Animated, {
5
5
  runOnJS,
6
6
  useAnimatedReaction,
7
7
  useAnimatedStyle,
8
+ useSharedValue,
8
9
  withSpring,
9
10
  withTiming,
10
11
  } from 'react-native-reanimated';
@@ -16,31 +17,40 @@ import {
16
17
  SPRING_CONFIGURATION,
17
18
  SPRING_CONFIGURATION_MENU,
18
19
  WINDOW_WIDTH,
20
+ WINDOW_HEIGHT,
19
21
  } from '../../constants';
20
22
  import { RenderCustomView } from '../menu/types';
23
+ import { calculateDynamicTransformValue } from '../../utils/calculations';
21
24
 
22
25
  const CustomViewComponent = () => {
23
- const { state, menuProps, customViewRef } = useInternal();
26
+ const { state, menuProps, customViewRef, safeAreaInsets } = useInternal();
24
27
 
25
28
  const [renderFn, setRenderFn] = useState<RenderCustomView | null>(null);
26
29
  const [customViewHeight, setCustomViewHeight] = useState(0);
27
30
  const [customViewWidth, setCustomViewWidth] = useState(0);
28
31
 
32
+ const isActive = useSharedValue(false);
33
+
29
34
  const closeMenu = useCallback(() => {
30
35
  state.value = CONTEXT_MENU_STATE.END;
31
36
  }, [state]);
32
37
 
33
- const onLayout = useCallback((event: LayoutChangeEvent) => {
34
- const { height, width } = event.nativeEvent.layout;
35
- setCustomViewHeight(height);
36
- setCustomViewWidth(width);
37
- }, []);
38
+ const onLayout = useCallback(
39
+ (event: LayoutChangeEvent) => {
40
+ const { height, width } = event.nativeEvent.layout;
41
+ setCustomViewHeight(height);
42
+ setCustomViewWidth(width);
43
+ menuProps.value = { ...menuProps.value, customViewHeight: height };
44
+ },
45
+ [menuProps]
46
+ );
38
47
 
39
48
  const clearRenderFn = useCallback(() => {
40
49
  setRenderFn(null);
41
50
  setCustomViewHeight(0);
42
51
  setCustomViewWidth(0);
43
- }, []);
52
+ menuProps.value = { ...menuProps.value, customViewHeight: 0 };
53
+ }, [menuProps]);
44
54
 
45
55
  const updateRenderFn = useCallback(
46
56
  (hasCustomView: boolean) => {
@@ -60,14 +70,33 @@ const CustomViewComponent = () => {
60
70
  }),
61
71
  ({ currentState, hasCustomView }) => {
62
72
  if (currentState === CONTEXT_MENU_STATE.ACTIVE && hasCustomView) {
73
+ isActive.value = true;
63
74
  runOnJS(updateRenderFn)(true);
64
75
  } else if (currentState === CONTEXT_MENU_STATE.END) {
76
+ isActive.value = false;
65
77
  runOnJS(updateRenderFn)(false);
66
78
  }
67
79
  },
68
80
  [state, menuProps]
69
81
  );
70
82
 
83
+ // Visibility style: depends ONLY on isActive, won't re-trigger on menuProps changes
84
+ const visibilityStyles = useAnimatedStyle(() => {
85
+ return {
86
+ opacity: withTiming(isActive.value ? 1 : 0, {
87
+ duration: HOLD_ITEM_TRANSFORM_DURATION,
88
+ }),
89
+ transform: [
90
+ {
91
+ scale: isActive.value
92
+ ? withSpring(1, SPRING_CONFIGURATION_MENU)
93
+ : withTiming(0, { duration: HOLD_ITEM_TRANSFORM_DURATION }),
94
+ },
95
+ ],
96
+ };
97
+ }, [isActive]);
98
+
99
+ // Positioning style: depends on menuProps and customViewHeight
71
100
  const wrapperStyles = useAnimatedStyle(() => {
72
101
  const anchorPositionVertical = menuProps.value.anchorPosition.split('-')[0];
73
102
  const isAbove = anchorPositionVertical === 'top';
@@ -94,7 +123,16 @@ const CustomViewComponent = () => {
94
123
  if (left < SCREEN_PADDING) {
95
124
  left = SCREEN_PADDING;
96
125
  }
97
- const tY = menuProps.value.transformValue;
126
+ const tY = calculateDynamicTransformValue(
127
+ menuProps.value.itemY,
128
+ menuProps.value.itemHeight,
129
+ menuProps.value.menuHeight,
130
+ menuProps.value.customViewHeight,
131
+ menuProps.value.anchorPosition,
132
+ WINDOW_HEIGHT,
133
+ safeAreaInsets?.top || 0,
134
+ safeAreaInsets?.bottom || 0
135
+ );
98
136
 
99
137
  // Positional offset via translateY:
100
138
  // above item: shift up by customViewHeight + gap
@@ -103,40 +141,15 @@ const CustomViewComponent = () => {
103
141
  ? -(customViewHeight + 8)
104
142
  : menuProps.value.itemHeight + 8;
105
143
 
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
144
  return {
122
145
  top,
123
146
  left,
124
147
  maxWidth: MAX_WIDTH,
125
- opacity: opacityAnimation,
126
148
  transform: [
127
149
  // 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
- },
150
+ { translateY: withSpring(tY, SPRING_CONFIGURATION) },
134
151
  // 2. Position offset (animated when customViewHeight changes)
135
152
  { 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
153
  ],
141
154
  };
142
155
  }, [menuProps, customViewHeight, customViewWidth]);
@@ -145,8 +158,10 @@ const CustomViewComponent = () => {
145
158
 
146
159
  return (
147
160
  <Animated.View style={[styles.customViewWrapper, wrapperStyles]}>
148
- <Animated.View onLayout={onLayout}>
149
- {renderFn({ closeMenu })}
161
+ <Animated.View style={visibilityStyles}>
162
+ <Animated.View onLayout={onLayout}>
163
+ {renderFn({ closeMenu })}
164
+ </Animated.View>
150
165
  </Animated.View>
151
166
  </Animated.View>
152
167
  );
@@ -35,6 +35,7 @@ import {
35
35
  TransformOriginAnchorPosition,
36
36
  getTransformOrigin,
37
37
  calculateMenuHeight,
38
+ calculateDynamicTransformValue,
38
39
  } from '../../utils/calculations';
39
40
  import {
40
41
  HOLD_ITEM_TRANSFORM_DURATION,
@@ -195,6 +196,7 @@ const HoldItemComponent = ({
195
196
  menuHeight: menuHeight,
196
197
  items: items || [],
197
198
  transformValue: transformValue.value,
199
+ customViewHeight: 0,
198
200
  actionParams: actionParams || {},
199
201
  hasCustomView: !!renderCustomView,
200
202
  };
@@ -332,7 +334,18 @@ const HoldItemComponent = ({
332
334
  const animateOpacity = () =>
333
335
  withDelay(HOLD_ITEM_TRANSFORM_DURATION, withTiming(0, { duration: 0 }));
334
336
 
335
- let tY = calculateTransformValue();
337
+ const screenH =
338
+ deviceOrientation === 'portrait' ? WINDOW_HEIGHT : WINDOW_WIDTH;
339
+ let tY = calculateDynamicTransformValue(
340
+ itemRectY.value,
341
+ itemRectHeight.value,
342
+ menuHeight,
343
+ menuProps.value.customViewHeight,
344
+ transformOrigin.value,
345
+ screenH,
346
+ safeAreaInsets?.top || 0,
347
+ safeAreaInsets?.bottom || 0
348
+ );
336
349
  const transformAnimation = () =>
337
350
  disableMove
338
351
  ? 0
@@ -14,10 +14,12 @@ import {
14
14
  HOLD_ITEM_TRANSFORM_DURATION,
15
15
  CONTEXT_MENU_STATE,
16
16
  SPRING_CONFIGURATION,
17
+ WINDOW_HEIGHT,
17
18
  } from '../../constants';
19
+ import { calculateDynamicTransformValue } from '../../utils/calculations';
18
20
 
19
21
  const MenuComponent = () => {
20
- const { state, menuProps } = useInternal();
22
+ const { state, menuProps, safeAreaInsets } = useInternal();
21
23
 
22
24
  const wrapperStyles = useAnimatedStyle(() => {
23
25
  const anchorPositionVertical = menuProps.value.anchorPosition.split('-')[0];
@@ -28,7 +30,16 @@ const MenuComponent = () => {
28
30
  : menuProps.value.itemY - 8;
29
31
  const left = menuProps.value.itemX;
30
32
  const width = menuProps.value.itemWidth;
31
- const tY = menuProps.value.transformValue;
33
+ const tY = calculateDynamicTransformValue(
34
+ menuProps.value.itemY,
35
+ menuProps.value.itemHeight,
36
+ menuProps.value.menuHeight,
37
+ menuProps.value.customViewHeight,
38
+ menuProps.value.anchorPosition,
39
+ WINDOW_HEIGHT,
40
+ safeAreaInsets?.top || 0,
41
+ safeAreaInsets?.bottom || 0
42
+ );
32
43
 
33
44
  return {
34
45
  top,
@@ -3,7 +3,6 @@ import { StyleSheet } from 'react-native';
3
3
 
4
4
  import Animated, {
5
5
  runOnJS,
6
- useAnimatedProps,
7
6
  useAnimatedReaction,
8
7
  useAnimatedStyle,
9
8
  useDerivedValue,
@@ -16,14 +15,12 @@ import {
16
15
  calculateMenuHeight,
17
16
  menuAnimationAnchor,
18
17
  } from '../../utils/calculations';
19
- import { BlurView } from '@react-native-community/blur';
20
18
 
21
19
  import MenuItems from './MenuItems';
22
20
 
23
21
  import {
24
22
  SPRING_CONFIGURATION_MENU,
25
23
  HOLD_ITEM_TRANSFORM_DURATION,
26
- IS_IOS,
27
24
  CONTEXT_MENU_STATE,
28
25
  } from '../../constants';
29
26
 
@@ -33,8 +30,6 @@ import { useInternal } from '../../hooks';
33
30
  import { deepEqual } from '../../utils/validations';
34
31
  import { leftOrRight } from './calculations';
35
32
 
36
- const AnimatedView = Animated.createAnimatedComponent(BlurView);
37
-
38
33
  const MenuListComponent = () => {
39
34
  const { state, theme, menuProps } = useInternal();
40
35
 
@@ -94,22 +89,12 @@ const MenuListComponent = () => {
94
89
  });
95
90
 
96
91
  const animatedInnerContainerStyle = useAnimatedStyle(() => {
92
+ const color = theme.value === 'light' ? '#fff' : '#1A1A1A';
97
93
  return {
98
- backgroundColor:
99
- theme.value === 'light'
100
- ? IS_IOS
101
- ? 'rgba(255, 255, 255, .75)'
102
- : 'rgba(255, 255, 255, .95)'
103
- : IS_IOS
104
- ? 'rgba(0,0,0,0.5)'
105
- : 'rgba(39, 39, 39, .8)',
94
+ backgroundColor: color,
106
95
  };
107
96
  }, [theme]);
108
97
 
109
- const animatedProps = useAnimatedProps(() => {
110
- return { blurType: theme.value };
111
- }, [theme]);
112
-
113
98
  const setter = (items: MenuItemProps[]) => {
114
99
  setItemList(items);
115
100
  prevList.value = items;
@@ -126,11 +111,7 @@ const MenuListComponent = () => {
126
111
  );
127
112
 
128
113
  return (
129
- <AnimatedView
130
- blurAmount={100}
131
- animatedProps={animatedProps}
132
- style={[styles.menuContainer, messageStyles]}
133
- >
114
+ <Animated.View style={[styles.menuContainer, messageStyles]}>
134
115
  <Animated.View
135
116
  style={[
136
117
  StyleSheet.absoluteFillObject,
@@ -140,7 +121,7 @@ const MenuListComponent = () => {
140
121
  >
141
122
  <MenuItems items={itemList} />
142
123
  </Animated.View>
143
- </AnimatedView>
124
+ </Animated.View>
144
125
  );
145
126
  };
146
127
 
@@ -28,6 +28,7 @@ export type MenuInternalProps = {
28
28
  anchorPosition: TransformOriginAnchorPosition;
29
29
  menuHeight: number;
30
30
  transformValue: number;
31
+ customViewHeight: number;
31
32
  actionParams: {
32
33
  [name: string]: (string | number)[];
33
34
  };
@@ -1,6 +1,10 @@
1
1
  import React, { memo, useEffect, useMemo, useRef } from 'react';
2
2
  import { PortalProvider } from '@gorhom/portal';
3
- import Animated, { useSharedValue, useAnimatedReaction, runOnJS } from 'react-native-reanimated';
3
+ import Animated, {
4
+ useSharedValue,
5
+ useAnimatedReaction,
6
+ runOnJS,
7
+ } from 'react-native-reanimated';
4
8
  import { GestureHandlerRootView } from 'react-native-gesture-handler';
5
9
 
6
10
  // Components
@@ -47,6 +51,7 @@ const ProviderComponent = ({
47
51
  anchorPosition: 'top-center',
48
52
  menuHeight: 0,
49
53
  transformValue: 0,
54
+ customViewHeight: 0,
50
55
  actionParams: {},
51
56
  hasCustomView: false,
52
57
  });
@@ -62,14 +67,12 @@ const ProviderComponent = ({
62
67
  state => {
63
68
  switch (state) {
64
69
  case CONTEXT_MENU_STATE.ACTIVE: {
65
- if (onOpen)
66
- runOnJS(onOpen)();
67
- break
70
+ if (onOpen) runOnJS(onOpen)();
71
+ break;
68
72
  }
69
73
  case CONTEXT_MENU_STATE.END: {
70
- if (onClose)
71
- runOnJS(onClose)();
72
- break
74
+ if (onClose) runOnJS(onClose)();
75
+ break;
73
76
  }
74
77
  }
75
78
  },
@@ -84,6 +84,59 @@ export const menuAnimationAnchor = (
84
84
  };
85
85
  };
86
86
 
87
+ export const calculateDynamicTransformValue = (
88
+ itemY: number,
89
+ itemHeight: number,
90
+ menuHeight: number,
91
+ customViewHeight: number,
92
+ anchorPosition: TransformOriginAnchorPosition,
93
+ screenHeight: number,
94
+ safeAreaTop: number,
95
+ safeAreaBottom: number
96
+ ): number => {
97
+ 'worklet';
98
+ const GAP = styleGuide.spacing;
99
+ const isAnchorTop = anchorPosition.includes('top');
100
+
101
+ if (isAnchorTop) {
102
+ // Menu below item, custom view above item
103
+ // Total space needed below item: itemHeight + gap + menuHeight + safeAreaBottom
104
+ // Total space needed above item: customViewHeight + gap + safeAreaTop
105
+ const bottomEdge = itemY + itemHeight + GAP + menuHeight + safeAreaBottom;
106
+ const topEdge = itemY - GAP - customViewHeight - safeAreaTop;
107
+
108
+ let tY = 0;
109
+
110
+ // If bottom overflows screen
111
+ if (bottomEdge > screenHeight) {
112
+ tY = screenHeight - bottomEdge;
113
+ }
114
+ // After shifting, check if custom view goes above safe area
115
+ if (topEdge + tY < 0) {
116
+ tY = -topEdge;
117
+ }
118
+ return tY;
119
+ } else {
120
+ // Menu above item, custom view below item
121
+ // Total space needed above item: menuHeight + gap + safeAreaTop
122
+ // Total space needed below item: itemHeight + gap + customViewHeight + safeAreaBottom
123
+ const topEdge = itemY - GAP - menuHeight - safeAreaTop;
124
+ const bottomEdge =
125
+ itemY + itemHeight + GAP + customViewHeight + safeAreaBottom;
126
+
127
+ let tY = 0;
128
+ // If top overflows screen (goes above safe area)
129
+ if (topEdge < 0) {
130
+ tY = -topEdge;
131
+ }
132
+ // After shifting, check if custom view goes below screen
133
+ if (bottomEdge + tY > screenHeight) {
134
+ tY = screenHeight - bottomEdge;
135
+ }
136
+ return tY;
137
+ }
138
+ };
139
+
87
140
  export const getTransformOrigin = (
88
141
  posX: number,
89
142
  itemWidth: number,