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.
- package/lib/commonjs/components/backdrop/Backdrop.js +3 -3
- package/lib/commonjs/components/backdrop/Backdrop.js.map +1 -1
- package/lib/commonjs/components/customView/CustomView.js +35 -25
- package/lib/commonjs/components/customView/CustomView.js.map +1 -1
- package/lib/commonjs/components/holdItem/HoldItem.js +3 -1
- package/lib/commonjs/components/holdItem/HoldItem.js.map +1 -1
- package/lib/commonjs/components/menu/Menu.js +5 -2
- package/lib/commonjs/components/menu/Menu.js.map +1 -1
- package/lib/commonjs/components/menu/MenuList.js +4 -13
- package/lib/commonjs/components/menu/MenuList.js.map +1 -1
- package/lib/commonjs/components/provider/Provider.js +1 -0
- package/lib/commonjs/components/provider/Provider.js.map +1 -1
- package/lib/commonjs/utils/calculations.js +48 -1
- package/lib/commonjs/utils/calculations.js.map +1 -1
- package/lib/module/components/backdrop/Backdrop.js +2 -2
- package/lib/module/components/backdrop/Backdrop.js.map +1 -1
- package/lib/module/components/customView/CustomView.js +36 -27
- package/lib/module/components/customView/CustomView.js.map +1 -1
- package/lib/module/components/holdItem/HoldItem.js +4 -2
- package/lib/module/components/holdItem/HoldItem.js.map +1 -1
- package/lib/module/components/menu/Menu.js +5 -3
- package/lib/module/components/menu/Menu.js.map +1 -1
- package/lib/module/components/menu/MenuList.js +6 -13
- package/lib/module/components/menu/MenuList.js.map +1 -1
- package/lib/module/components/provider/Provider.js +1 -0
- package/lib/module/components/provider/Provider.js.map +1 -1
- package/lib/module/utils/calculations.js +44 -0
- package/lib/module/utils/calculations.js.map +1 -1
- package/lib/typescript/components/menu/MenuList.d.ts +1 -1
- package/lib/typescript/components/menu/types.d.ts +1 -0
- package/lib/typescript/utils/calculations.d.ts +1 -0
- package/package.json +1 -1
- package/src/components/customView/CustomView.tsx +51 -36
- package/src/components/holdItem/HoldItem.tsx +14 -1
- package/src/components/menu/Menu.tsx +13 -2
- package/src/components/menu/MenuList.tsx +4 -23
- package/src/components/menu/types.d.ts +1 -0
- package/src/components/provider/Provider.tsx +10 -7
- package/src/utils/calculations.ts +53 -0
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"sources":["MenuList.tsx"],"names":["React","StyleSheet","Animated","runOnJS","
|
|
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"]}
|
|
@@ -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":"AAAA,OAAOA,KAAP,IAAgBC,IAAhB,EAAsBC,SAAtB,EAAiCC,OAAjC,EAA0CC,MAA1C,QAAwD,OAAxD;AACA,SAASC,cAAT,QAA+B,gBAA/B;AACA,OAAOC,QAAP,
|
|
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":"AAAA,OAAOA,UAAP,MAAuB,eAAvB;AACA,SACEC,UADF,EAEEC,+BAFF,EAGEC,UAHF,QAIO,cAJP;AAMA,OAAO,MAAMC,cAAc,GAAG,MAAM;AAClC;;AACA,SACEJ,UAAU,CAACK,UAAX,CAAsBC,OAAtB,CAA8BC,UAA9B,GAA2CJ,UAA3C,GACAH,UAAU,CAACQ,OAAX,GAAqB,GAFvB;AAID,CANM;AAQP,OAAO,MAAMC,mBAAmB,GAAG,CACjCC,UADiC,EAEjCC,cAFiC,KAG9B;AACH;;AACA,SACEP,cAAc,KAAKM,UAAnB,IACCA,UAAU,GAAG,CADd,IAEAC,cAAc,GAAGX,UAAU,CAACQ,OAH9B;AAKD,CAVM;AAoBP,OAAO,MAAMI,mBAAmB,GAAG,CACjCC,WADiC,EAEjCC,SAFiC,EAGjCJ,UAHiC,EAIjCK,wBAJiC,KAK9B;AACH;;AACA,QAAMC,UAAU,GAAGP,mBAAmB,CAACC,UAAD,EAAaK,wBAAb,CAAtC;AACA,QAAME,kBAA4B,GAAGJ,WAAW,CAACK,KAAZ,CAAkB,GAAlB,CAArC;AAEA,QAAMC,OAAO,GAAGL,SAAhB;AACA,QAAMM,OAAO,GAAG,CAAhB;AAEA,QAAMC,MAAM,GAAG,EAAEL,UAAU,GAAG,CAAf,CAAf;AACA,QAAMM,MAAM,GAAGN,UAAU,GAAG,CAA5B;AAEA,QAAMO,OAAO,GAAItB,UAAU,GAAG,CAAd,GAAmB,CAAC,CAApC;AACA,QAAMuB,OAAO,GAAIvB,UAAU,GAAG,CAAd,GAAmB,CAAnC;AAEA,SAAO;AACLwB,IAAAA,wBAAwB,EAAE;AACxBC,MAAAA,UAAU,EACRT,kBAAkB,CAAC,CAAD,CAAlB,KAA0B,OAA1B,GACI,CAACM,OADL,GAEIN,kBAAkB,CAAC,CAAD,CAAlB,KAA0B,MAA1B,GACAM,OADA,GAEAJ,OANkB;AAOxBQ,MAAAA,UAAU,EACRV,kBAAkB,CAAC,CAAD,CAAlB,KAA0B,KAA1B,GACII,MADJ,GAEIJ,kBAAkB,CAAC,CAAD,CAAlB,KAA0B,QAA1B,GACAI,MADA,GAEAD;AAZkB,KADrB;AAeLQ,IAAAA,qBAAqB,EAAE;AACrBF,MAAAA,UAAU,EACRT,kBAAkB,CAAC,CAAD,CAAlB,KAA0B,OAA1B,GACI,CAACO,OADL,GAEIP,kBAAkB,CAAC,CAAD,CAAlB,KAA0B,MAA1B,GACAO,OADA,GAEAJ,OANe;AAOrBO,MAAAA,UAAU,EACRV,kBAAkB,CAAC,CAAD,CAAlB,KAA0B,KAA1B,GACIK,MADJ,GAEIL,kBAAkB,CAAC,CAAD,CAAlB,KAA0B,QAA1B,GACA,CAACK,MADD,GAEAF;AAZe;AAflB,GAAP;AA8BD,CAjDM;AAmDP,OAAO,MAAMS,kBAAkB,GAAG,CAChCC,IADgC,
|
|
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"]}
|
|
@@ -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.
|
|
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(
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
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 =
|
|
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
|
|
149
|
-
{
|
|
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
|
-
|
|
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 =
|
|
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
|
-
<
|
|
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
|
-
</
|
|
124
|
+
</Animated.View>
|
|
144
125
|
);
|
|
145
126
|
};
|
|
146
127
|
|
|
@@ -1,6 +1,10 @@
|
|
|
1
1
|
import React, { memo, useEffect, useMemo, useRef } from 'react';
|
|
2
2
|
import { PortalProvider } from '@gorhom/portal';
|
|
3
|
-
import Animated, {
|
|
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
|
-
|
|
67
|
-
break
|
|
70
|
+
if (onOpen) runOnJS(onOpen)();
|
|
71
|
+
break;
|
|
68
72
|
}
|
|
69
73
|
case CONTEXT_MENU_STATE.END: {
|
|
70
|
-
if (onClose)
|
|
71
|
-
|
|
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,
|