import * as React from 'react'; import { css } from '@patternfly/react-styles'; import { KeyTypes } from '../../../helpers/constants'; import styles from '@patternfly/react-styles/css/components/OptionsMenu/options-menu'; import { DropdownContext } from '../Dropdown'; export interface OptionsMenuToggleWithTextProps extends React.HTMLProps { /** Id of the parent options menu component */ parentId?: string; /** Content to be rendered inside the options menu toggle as text or another non-interactive element */ toggleText: React.ReactNode; /** classes to be added to the options menu toggle text */ toggleTextClassName?: string; /** Content to be rendered inside the options menu toggle button */ toggleButtonContents?: React.ReactNode; /** Classes to be added to the options menu toggle button */ toggleButtonContentsClassName?: string; /** Callback for when this options menu is toggled */ onToggle?: ( event: MouseEvent | TouchEvent | KeyboardEvent | React.KeyboardEvent | React.MouseEvent, isOpen: boolean ) => void; /** Inner function to indicate open on Enter */ onEnter?: (event: React.MouseEvent | React.KeyboardEvent) => void; /** Flag to indicate if menu is open */ isOpen?: boolean; /** Flag to indicate if the button is plain */ isPlain?: boolean; /** Forces display of the active state of the options menu button */ isActive?: boolean; /** Disables the options menu toggle */ isDisabled?: boolean; /** @hide Internal parent reference */ parentRef?: React.RefObject; /** Indicates that the element has a popup context menu or sub-level menu */ 'aria-haspopup'?: boolean | 'dialog' | 'menu' | 'listbox' | 'tree' | 'grid'; /** Provides an accessible name for the button when an icon is used instead of text */ 'aria-label'?: string; /** @hide Display the toggle in text only mode. */ isText?: boolean; /** @hide The menu element */ getMenuRef?: () => HTMLElement; } export const OptionsMenuToggleWithText: React.FunctionComponent = ({ parentId = '', toggleText, toggleTextClassName = '', toggleButtonContents, toggleButtonContentsClassName = '', onToggle = () => null as any, isOpen = false, isPlain = false, /* eslint-disable @typescript-eslint/no-unused-vars */ isText = true, isDisabled = false, /* eslint-disable @typescript-eslint/no-unused-vars */ isActive = false, 'aria-haspopup': ariaHasPopup, parentRef, /* eslint-disable @typescript-eslint/no-unused-vars */ getMenuRef, onEnter, /* eslint-enable @typescript-eslint/no-unused-vars */ 'aria-label': ariaLabel = 'Options menu', ...props }: OptionsMenuToggleWithTextProps) => { const buttonRef = React.useRef(); React.useEffect(() => { document.addEventListener('mousedown', onDocClick); document.addEventListener('touchstart', onDocClick); document.addEventListener('keydown', onEscPress); return () => { document.removeEventListener('mousedown', onDocClick); document.removeEventListener('touchstart', onDocClick); document.removeEventListener('keydown', onEscPress); }; }); const onDocClick = (event: MouseEvent | TouchEvent) => { if (isOpen && parentRef && parentRef.current && !parentRef.current.contains(event.target as Node)) { onToggle(event, false); buttonRef.current.focus(); } }; const onKeyDown = (event: React.KeyboardEvent) => { if (event.key === 'Tab' && !isOpen) { return; } event.preventDefault(); if ((event.key === 'Enter' || event.key === ' ') && isOpen) { onToggle(event, !isOpen); } else if ((event.key === 'Enter' || event.key === ' ') && !isOpen) { onToggle(event, !isOpen); onEnter(event); } }; const onEscPress = (event: KeyboardEvent) => { if ( isOpen && (event.key === KeyTypes.Escape || event.key === 'Tab') && parentRef && parentRef.current && parentRef.current.contains(event.target as Node) ) { onToggle(event, false); buttonRef.current.focus(); } }; return ( {({ id: contextId }) => (
{toggleText}
)}
); }; OptionsMenuToggleWithText.displayName = 'OptionsMenuToggleWithText';