@primer/components
Version:
Primer react components
100 lines (92 loc) • 2.89 kB
JavaScript
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
});