UNPKG

@primer/components

Version:
100 lines (92 loc) 2.89 kB
import Button from './Button'; import React from 'react'; import { AnchoredOverlay } from './AnchoredOverlay'; import { useProvidedRefOrCreate, useProvidedStateOrCreate } from './hooks'; import { Divider } from './ActionList2/Divider'; import { ActionListContainerContext } from './ActionList2/ActionListContainerContext'; export const MenuContext = /*#__PURE__*/React.createContext({ renderAnchor: null, open: false }); const Menu = ({ anchorRef: externalAnchorRef, open, onOpenChange, children }) => { const [combinedOpenState, setCombinedOpenState] = useProvidedStateOrCreate(open, onOpenChange, false); const onOpen = React.useCallback(() => setCombinedOpenState(true), [setCombinedOpenState]); const onClose = React.useCallback(() => setCombinedOpenState(false), [setCombinedOpenState]); const anchorRef = useProvidedRefOrCreate(externalAnchorRef); let renderAnchor = null; // 🚨 Hack for good API! // we strip out Anchor from children and pass it to AnchoredOverlay to render // with additional props for accessibility const contents = React.Children.map(children, child => { if (child.type === MenuButton || child.type === Anchor) { renderAnchor = anchorProps => /*#__PURE__*/React.cloneElement(child, anchorProps); return null; } return child; }); return /*#__PURE__*/React.createElement(MenuContext.Provider, { value: { anchorRef, renderAnchor, open: combinedOpenState, onOpen, onClose } }, contents); }; Menu.displayName = "Menu"; const Anchor = /*#__PURE__*/React.forwardRef(({ children, ...anchorProps }, anchorRef) => { return /*#__PURE__*/React.cloneElement(children, { ...anchorProps, ref: anchorRef }); }); /** this component is syntactical sugar 🍭 */ const MenuButton = /*#__PURE__*/React.forwardRef((props, anchorRef) => { return /*#__PURE__*/React.createElement(Anchor, { ref: anchorRef }, /*#__PURE__*/React.createElement(Button, props)); }); const Overlay = ({ children, ...overlayProps }) => { // we typecast anchorRef as required instead of optional // because we know that we're setting it in context in Menu const { anchorRef, renderAnchor, open, onOpen, onClose } = React.useContext(MenuContext); return /*#__PURE__*/React.createElement(AnchoredOverlay, { anchorRef: anchorRef, renderAnchor: renderAnchor, open: open, onOpen: onOpen, onClose: onClose, overlayProps: overlayProps }, /*#__PURE__*/React.createElement(ActionListContainerContext.Provider, { value: { container: 'ActionMenu', listRole: 'menu', itemRole: 'menuitem', afterSelect: onClose } }, children)); }; Overlay.displayName = "Overlay"; Menu.displayName = 'ActionMenu'; export const ActionMenu = Object.assign(Menu, { Button: MenuButton, Anchor, Overlay, Divider });