import { Menu as ReachMenu, MenuButton as ReachMenuButton, MenuItem as ReachMenuItem, MenuItems as ReachMenuItems, MenuItemsProps as ReachMenuItemsProps, MenuLink as ReachMenuLink, MenuPopover as ReachMenuPopover, } from '@reach/menu-button' import { NavigationChevronDown } from '@repay/cactus-icons' import { ColorStyle } from '@repay/cactus-theme' import PropTypes from 'prop-types' import React from 'react' import styled, { createGlobalStyle, css } from 'styled-components' import { margin, MarginProps } from 'styled-system' import { extractMargins } from '../helpers/omit' import { positionDropDown, usePositioning } from '../helpers/positionPopover' import { border, popupBoxShadow, popupShape, radius, textStyle } from '../helpers/theme' const MenuButtonStyles = createGlobalStyle` :root { --reach-menu-button: 1; } ` const menuListVariants: VariantMap = { filled: css` border: 0; &[data-selected] { ${(p): ColorStyle => p.theme.colorStyles.callToAction}; } `, unfilled: css` border: ${(p) => border(p.theme, 'white')}; &[data-selected] { border-color: ${(p): string => p.theme.colors.callToAction}; } &:hover { ${(p): ColorStyle => p.theme.colorStyles.callToAction}; } `, } interface MenuListProps extends ReachMenuItemsProps { variant: MenuButtonVariant } const MenuList = styled(ReachMenuItems)` padding: 8px 0; outline: none; ${(p) => popupShape('menu', p.theme.shape)} ${(p) => popupBoxShadow(p.theme)} background-color: ${(p) => p.theme.colors.white}; [data-reach-menu-item] { box-sizing: border-box; position: relative; display: block; cursor: pointer; text-decoration: none; overflow-wrap: break-word; ${(p) => textStyle(p.theme, 'small')}; ${(p) => p.theme.colorStyles.standard}; outline: none; padding: 4px 16px; text-align: center; ${(p) => menuListVariants[p.variant]} } ` type MenuButtonVariant = 'filled' | 'unfilled' interface DropDownProps { isOpen: boolean anchorRef: React.RefObject variant: MenuButtonVariant children?: React.ReactNode } interface MenuButtonProps extends MarginProps { label: React.ReactNode variant?: MenuButtonVariant /** * Must be a MenuButton.Item or MenuButton.Link */ children: React.ReactNode disabled?: boolean } type MenuButtonType = React.FC & { Item: typeof ReachMenuItem Link: typeof ReachMenuLink } export const MenuButton: MenuButtonType = (props) => { const { label, children, variant = 'filled', ...rest } = props const marginProps = extractMargins(rest) const anchorRef = React.useRef(null) return ( {({ isOpen }) => ( <> {label} {children} )} ) } const DropDown: React.FC = ({ isOpen, variant, anchorRef, children }) => { const ref = React.useRef(null) usePositioning({ position: positionDropDown, visible: isOpen, ref, anchorRef, updateOnScroll: true, }) return ( {children} ) } MenuButton.Item = ReachMenuItem MenuButton.Link = ReachMenuLink type VariantMap = { [K in MenuButtonVariant]: ReturnType } const buttonVariantMap: VariantMap = { filled: css` color: ${(p): string => p.theme.colors.white}; background-color: ${(p): string => p.theme.colors.darkContrast}; border-color: ${(p): string => p.theme.colors.darkContrast}; &:hover, &[aria-expanded='true'] { background-color: ${(p): string => p.theme.colors.callToAction}; border-color: ${(p): string => p.theme.colors.callToAction}; } &:focus { ::after { content: ''; display: block; position: absolute; height: calc(100% + 10px); width: calc(100% + 11px); top: -5px; left: -5px; box-sizing: border-box; border-radius: ${radius(20)}; border: ${(p) => border(p.theme, 'callToAction')}; } } `, unfilled: css` color: ${(p): string => p.theme.colors.darkContrast}; background-color: ${(p): string => p.theme.colors.white}; border-color: ${(p): string => p.theme.colors.white}; &:hover, &[aria-expanded='true'] { color: ${(p): string => p.theme.colors.white}; background-color: ${(p): string => p.theme.colors.base}; border-color: ${(p): string => p.theme.colors.base}; } &:focus { border-color: ${(p): string => p.theme.colors.callToAction}; } `, } const StyledButton = styled.button` position: relative; box-sizing: border-box; width: 100%; border-radius: ${radius(20)}; border: ${(p) => border(p.theme, '')}; padding: 2px 24px 2px 14px; outline: none; cursor: pointer; appearance: none; overflow: visible; ${(p) => textStyle(p.theme, 'body')}; ${(p) => buttonVariantMap[p.variant || 'filled']} &::-moz-focus-inner { border: 0; } &:disabled { ${(p): ColorStyle => p.theme.colorStyles.disable}; border-color: ${(p): string => p.theme.colors.lightGray}; cursor: not-allowed; } &[aria-expanded='true'] { ${NavigationChevronDown} { transform: rotate3d(1, 0, 0, 180deg); } } ${NavigationChevronDown} { position: absolute; right: 10px; // 8 + 2px from border top: 12px; } ` const Wrapper = styled.div` position: relative; display: inline-block; max-width: 100%; ${margin} ` const StyledPopover = styled(ReachMenuPopover)` position: fixed; z-index: 1000; ` MenuButton.propTypes = { label: PropTypes.node.isRequired, children: PropTypes.node.isRequired, disabled: PropTypes.bool, variant: PropTypes.oneOf(['filled', 'unfilled']), } MenuButton.defaultProps = { variant: 'filled', } export default MenuButton