@razorpay/blade
Version:
The Design System that powers Razorpay
179 lines (174 loc) • 6.34 kB
JavaScript
import _slicedToArray from '@babel/runtime/helpers/slicedToArray';
import React__default from 'react';
import { useFloatingTree, useFloatingNodeId, useFloatingParentNodeId, useListItem, useFloating, offset, flip, shift, size, autoUpdate, useHover, safePolygon, useClick, useRole, useDismiss, useListNavigation, useInteractions, useTransitionStyles } from '@floating-ui/react';
import { useControllableState } from '../../utils/useControllable.js';
import { OVERLAY_OFFSET, OVERLAY_TRANSITION_OFFSET } from '../BaseMenu/tokens.js';
import '../../utils/index.js';
import useTheme from '../BladeProvider/useTheme.js';
import { makeSize } from '../../utils/makeSize/makeSize.js';
var MenuContext = /*#__PURE__*/React__default.createContext({
getItemProps: function getItemProps() {
return {};
},
// eslint-disable-next-line @typescript-eslint/no-empty-function
setHasFocusInside: function setHasFocusInside() {},
isOpen: false
});
var useMenu = function useMenu() {
var contextValue = React__default.useContext(MenuContext);
return contextValue;
};
var useFloatingMenuSetup = function useFloatingMenuSetup(_ref) {
var elementsRef = _ref.elementsRef,
openInteraction = _ref.openInteraction,
onOpenChange = _ref.onOpenChange,
isOpen = _ref.isOpen;
var _useControllableState = useControllableState({
value: isOpen,
defaultValue: false,
onChange: function onChange(isOpen) {
return onOpenChange === null || onOpenChange === void 0 ? void 0 : onOpenChange({
isOpen: isOpen
});
}
}),
_useControllableState2 = _slicedToArray(_useControllableState, 2),
isControllableOpen = _useControllableState2[0],
setIsControllableOpen = _useControllableState2[1];
var _React$useState = React__default.useState(null),
_React$useState2 = _slicedToArray(_React$useState, 2),
activeIndex = _React$useState2[0],
setActiveIndex = _React$useState2[1];
var tree = useFloatingTree();
var nodeId = useFloatingNodeId();
var parentId = useFloatingParentNodeId();
var item = useListItem();
var _useTheme = useTheme(),
theme = _useTheme.theme;
var isNested = parentId != null;
var _useFloating = useFloating({
nodeId: nodeId,
open: isControllableOpen,
onOpenChange: function onOpenChange(_isOpen) {
return setIsControllableOpen(function () {
return _isOpen;
});
},
placement: isNested ? 'right-start' : 'bottom-start',
middleware: [offset({
mainAxis: isNested ? 12 : OVERLAY_OFFSET,
alignmentAxis: isNested ? -16 : 0
}), flip(), shift(), size({
apply: function apply(_ref2) {
var availableHeight = _ref2.availableHeight,
elements = _ref2.elements;
elements.floating.style.maxHeight = "".concat(availableHeight, "px");
}
})],
whileElementsMounted: autoUpdate
}),
floatingStyles = _useFloating.floatingStyles,
refs = _useFloating.refs,
context = _useFloating.context;
var hover = useHover(context, {
enabled: isNested || openInteraction === 'hover',
delay: {
open: 75
},
handleClose: safePolygon({
blockPointerEvents: true
})
});
var click = useClick(context, {
event: 'mousedown',
toggle: !isNested,
ignoreMouse: isNested
});
var role = useRole(context, {
role: 'menu'
});
var dismiss = useDismiss(context, {
bubbles: true
});
var listNavigation = useListNavigation(context, {
listRef: elementsRef,
activeIndex: activeIndex,
nested: isNested,
onNavigate: setActiveIndex,
focusItemOnHover: false
});
var _useInteractions = useInteractions([hover, click, role, dismiss, listNavigation]),
getReferenceProps = _useInteractions.getReferenceProps,
getFloatingProps = _useInteractions.getFloatingProps,
getItemProps = _useInteractions.getItemProps;
var _useTransitionStyles = useTransitionStyles(context, {
duration: theme.motion.duration.quick,
initial: function initial(_ref3) {
var side = _ref3.side;
var transitionOffset = makeSize(OVERLAY_TRANSITION_OFFSET);
var transformMap = {
top: "translateY(".concat(transitionOffset, ")"),
right: "translateX(-".concat(transitionOffset, ")"),
left: "translateX(".concat(transitionOffset, ")"),
bottom: "translateY(-".concat(transitionOffset, ")")
};
return {
transform: transformMap[side !== null && side !== void 0 ? side : 'bottom'],
opacity: 0
};
}
}),
isMounted = _useTransitionStyles.isMounted,
floatingTransitionStyles = _useTransitionStyles.styles;
// Event emitter allows you to communicate across tree components.
// This effect closes all menus when an item gets clicked anywhere
// in the tree.
React__default.useEffect(function () {
if (!tree) return;
var handleTreeClick = function handleTreeClick() {
setIsControllableOpen(function () {
return false;
});
};
var onSubMenuOpen = function onSubMenuOpen(event) {
if (event.nodeId !== nodeId && event.parentId === parentId) {
setIsControllableOpen(function () {
return false;
});
}
};
tree.events.on('click', handleTreeClick);
tree.events.on('menuopen', onSubMenuOpen);
// eslint-disable-next-line consistent-return
return function () {
// Cleanup
tree.events.off('click', handleTreeClick);
tree.events.off('menuopen', onSubMenuOpen);
};
// eslint-disable-next-line react-hooks/exhaustive-deps
}, [tree, nodeId, parentId]);
React__default.useEffect(function () {
if (isOpen && tree) {
tree.events.emit('menuopen', {
parentId: parentId,
nodeId: nodeId
});
}
}, [tree, isOpen, nodeId, parentId]);
return {
getReferenceProps: getReferenceProps,
getFloatingProps: getFloatingProps,
getItemProps: getItemProps,
item: item,
context: context,
nodeId: nodeId,
isOpen: isControllableOpen,
floatingStyles: floatingStyles,
refs: refs,
isNested: isNested,
isMounted: isMounted,
floatingTransitionStyles: floatingTransitionStyles
};
};
export { MenuContext, useFloatingMenuSetup, useMenu };
//# sourceMappingURL=useMenu.js.map