UNPKG

stream-chat-react

Version:

React components to create chat conversations or livestream style chat

89 lines (88 loc) 3.98 kB
import clsx from 'clsx'; import React, { useCallback, useEffect, useMemo, useRef, useState } from 'react'; import { useDialogIsOpen, useDialogOnNearestManager } from './hooks'; import { useDialogAnchor } from './DialogAnchor'; export const ButtonWithSubmenu = ({ children, className, placement, Submenu, submenuContainerProps, ...buttonProps }) => { const buttonRef = useRef(null); const [dialogContainer, setDialogContainer] = useState(null); const keepSubmenuOpen = useRef(false); const dialogCloseTimeout = useRef(null); const dialogId = useMemo(() => `submenu-${Math.random().toString(36).slice(2)}`, []); const { dialog, dialogManager } = useDialogOnNearestManager({ id: dialogId }); const dialogIsOpen = useDialogIsOpen(dialogId, dialogManager?.id); const { setPopperElement, styles } = useDialogAnchor({ open: dialogIsOpen, placement, referenceElement: buttonRef.current, }); const closeDialogLazily = useCallback(() => { if (dialogCloseTimeout.current) clearTimeout(dialogCloseTimeout.current); dialogCloseTimeout.current = setTimeout(() => { if (keepSubmenuOpen.current) return; dialog.close(); }, 100); }, [dialog]); const handleClose = useCallback((event) => { const parentButton = buttonRef.current; if (!dialogIsOpen || !parentButton) return; event.stopPropagation(); closeDialogLazily(); parentButton.focus(); }, [closeDialogLazily, dialogIsOpen, buttonRef]); const handleFocusParentButton = () => { if (dialogIsOpen) return; dialog.open(); keepSubmenuOpen.current = true; }; useEffect(() => { const parentButton = buttonRef.current; if (!dialogIsOpen || !parentButton) return; const hideOnEscape = (event) => { if (event.key !== 'Escape') return; handleClose(event); keepSubmenuOpen.current = false; }; document.addEventListener('keyup', hideOnEscape, { capture: true }); return () => { document.removeEventListener('keyup', hideOnEscape, { capture: true }); }; }, [dialogIsOpen, handleClose]); return (React.createElement(React.Fragment, null, React.createElement("button", { "aria-selected": 'false', className: clsx(className, 'str_chat__button-with-submenu', { 'str_chat__button-with-submenu--submenu-open': dialogIsOpen, }), onBlur: () => { keepSubmenuOpen.current = false; closeDialogLazily(); }, onClick: (event) => { event.stopPropagation(); dialog.toggle(); }, onFocus: handleFocusParentButton, onMouseEnter: handleFocusParentButton, onMouseLeave: () => { keepSubmenuOpen.current = false; closeDialogLazily(); }, ref: buttonRef, role: 'option', ...buttonProps }, children), dialogIsOpen && (React.createElement("div", { onBlur: (event) => { const isBlurredDescendant = event.relatedTarget instanceof Node && dialogContainer?.contains(event.relatedTarget); if (isBlurredDescendant) return; keepSubmenuOpen.current = false; closeDialogLazily(); }, onFocus: () => { keepSubmenuOpen.current = true; }, onMouseEnter: () => { keepSubmenuOpen.current = true; }, onMouseLeave: () => { keepSubmenuOpen.current = false; closeDialogLazily(); }, ref: (element) => { setPopperElement(element); setDialogContainer(element); }, style: styles, tabIndex: -1, ...submenuContainerProps }, React.createElement(Submenu, null))))); };