import React from "react"; import PropTypes from "prop-types"; import { defineMessages, injectIntl } from "react-intl"; import { Divider } from "@mui/material"; import Icon from "/imports/ui/components/common/icon/component"; import { SMALL_VIEWPORT_BREAKPOINT } from '/imports/ui/components/layout/enums'; import KEY_CODES from '/imports/utils/keyCodes'; import MenuSkeleton from './skeleton'; import GenericContentItem from '/imports/ui/components/generic-content/generic-content-item/component'; import Styled from './styles'; const intlMessages = defineMessages({ close: { id: 'app.dropdown.close', description: 'Close button label', }, active: { id: 'app.dropdown.list.item.activeLabel', description: 'active item label', }, }); class BBBMenu extends React.Component { constructor(props) { super(props); this.state = { anchorEl: null, }; this.optsToMerge = {}; this.autoFocus = false; this.handleKeyDown = this.handleKeyDown.bind(this); this.handleClick = this.handleClick.bind(this); this.handleClose = this.handleClose.bind(this); } componentDidUpdate() { const { anchorEl } = this.state; const { open } = this.props; if (open === false && anchorEl) { this.setState({ anchorEl: null }); } else if (open === true && !anchorEl) { this.setState({ anchorEl: this.anchorElRef }); } } handleKeyDown(event) { const { anchorEl } = this.state; const { isHorizontal } = this.props; const isMenuOpen = Boolean(anchorEl); const previousKey = isHorizontal ? KEY_CODES.ARROW_LEFT : KEY_CODES.ARROW_UP; const nextKey = isHorizontal ? KEY_CODES.ARROW_RIGHT : KEY_CODES.ARROW_DOWN; if ([KEY_CODES.ESCAPE, KEY_CODES.TAB].includes(event.which)) { this.handleClose(); return; } if (isMenuOpen && [previousKey, nextKey].includes(event.which)) { event.preventDefault(); event.stopPropagation(); const menuItems = Array.from(document.querySelectorAll('[data-key^="menuItem-"]')); if (menuItems.length === 0) return; const focusedIndex = menuItems.findIndex(item => item === document.activeElement); const nextIndex = event.which === previousKey ? focusedIndex - 1 : focusedIndex + 1; let indexToFocus = 0; if (nextIndex < 0) { indexToFocus = menuItems.length - 1; } else if (nextIndex >= menuItems.length) { indexToFocus = 0; } else { indexToFocus = nextIndex; } menuItems[indexToFocus].focus(); } }; handleClick(event) { this.setState({ anchorEl: event.currentTarget }); }; handleClose(event) { const { onCloseCallback } = this.props; this.setState({ anchorEl: null }, onCloseCallback()); if (event) { event.persist(); if (event.type === 'click') { setTimeout(() => { document.activeElement.blur(); }, 0); } } }; makeMenuItems() { const { actions, selectedEmoji, intl, isHorizontal, isEmoji, isMobile, roundButtons, keepOpen } = this.props; return actions?.map(a => { const { dataTest, label, onClick, key, disabled, description, selected, textColor, isToggle, loading, isTitle, titleActions, contentFunction } = a; const emojiSelected = key?.toLowerCase()?.includes(selectedEmoji?.toLowerCase()); let customStyles = { paddingLeft: '16px', paddingRight: '16px', paddingTop: '12px', paddingBottom: '12px', marginLeft: '0px', marginRight: '0px', }; if (a.customStyles) { customStyles = { ...customStyles, ...a.customStyles }; } if (loading) { return ( ); } return [ (!a.isSeparator && onClick) && ( { onClick(event); const close = !keepOpen && !key?.includes('setstatus') && !key?.includes('back'); // prevent menu close for sub menu actions if (close) this.handleClose(event); event.stopPropagation(); }}> {a.icon ? : null} {label} {description &&
{`${description}${selected ? ` - ${intl.formatMessage(intlMessages.active)}` : ''}`}
} {a.iconRight ? : null}
), (!onClick && !a.isSeparator) && ( {!contentFunction ? ( <> {a.icon ? : null} {label} {a.iconRight ? : null} {(isTitle && titleActions?.length > 0) ? ( titleActions.map((item, index) => ( )) ) : null} ) : ( )} ), a.isSeparator && ]; }) ?? []; } render() { const { anchorEl } = this.state; const { trigger, intl, customStyles, dataTest, opts, accessKey, open, renderOtherComponents, customAnchorEl, hasRoundedCorners, overrideMobileStyles, isHorizontal, } = this.props; const actionsItems = this.makeMenuItems(); const roundedCornersStyles = { borderRadius: '3rem' }; let menuStyles = { zIndex: 999 }; if (customStyles) { menuStyles = { ...menuStyles, ...customStyles }; } if (isHorizontal) { const horizontalStyles = { display: 'flex' }; menuStyles = { ...menuStyles, ...horizontalStyles}; } return ( <>
{ e.persist(); const firefoxInputSource = !([1, 5].includes(e.nativeEvent.mozInputSource)); // 1 = mouse, 5 = touch (firefox only) const chromeInputSource = !(['mouse', 'touch'].includes(e.nativeEvent.pointerType)); this.optsToMerge.autoFocus = firefoxInputSource && chromeInputSource; this.handleClick(e); }} onKeyPress={(e) => { e.persist(); if (e.which !== KEY_CODES.ENTER) return null; this.handleClick(e); }} accessKey={accessKey} ref={(ref) => this.anchorElRef = ref} role="button" tabIndex={-1} > {trigger}
{actionsItems} {renderOtherComponents} {!overrideMobileStyles && anchorEl && window.innerWidth < SMALL_VIEWPORT_BREAKPOINT && } ); } } BBBMenu.defaultProps = { opts: { id: "default-dropdown-menu", autoFocus: false, keepMounted: true, transitionDuration: 0, elevation: 3, getcontentanchorel: null, fullwidth: "true", anchorOrigin: { vertical: 'top', horizontal: 'right' }, transformorigin: { vertical: 'top', horizontal: 'right' }, }, onCloseCallback: () => { }, dataTest: '', }; BBBMenu.propTypes = { intl: PropTypes.shape({ formatMessage: PropTypes.func.isRequired, }).isRequired, trigger: PropTypes.element.isRequired, actions: PropTypes.array.isRequired, onCloseCallback: PropTypes.func, dataTest: PropTypes.string, open: PropTypes.bool, customStyles: PropTypes.object, opts: PropTypes.object, accessKey: PropTypes.string, }; export default injectIntl(BBBMenu);