Merge branch 'dropdown-menu-improvements' into 'develop'

Dropdown menu improvements and fixes

See merge request soapbox-pub/soapbox!2541
This commit is contained in:
marcin mikołajczak 2023-06-02 20:42:34 +00:00
commit ea298472a8
2 changed files with 27 additions and 44 deletions

View file

@ -87,7 +87,7 @@ const DropdownMenuItem = ({ index, item, onClick }: IDropdownMenuItem) => {
title={item.text} title={item.text}
className={ className={
clsx({ clsx({
'flex px-4 py-2.5 text-sm text-gray-700 dark:text-gray-500 hover:bg-gray-100 dark:hover:bg-gray-800 focus:outline-none cursor-pointer': true, 'flex px-4 py-2.5 text-sm text-gray-700 dark:text-gray-500 hover:bg-gray-100 dark:hover:bg-gray-800 focus:bg-gray-100 dark:focus:bg-gray-800 focus:outline-none cursor-pointer': true,
'text-danger-600 dark:text-danger-400': item.destructive, 'text-danger-600 dark:text-danger-400': item.destructive,
}) })
} }
@ -106,4 +106,4 @@ const DropdownMenuItem = ({ index, item, onClick }: IDropdownMenuItem) => {
); );
}; };
export default DropdownMenuItem; export default DropdownMenuItem;

View file

@ -4,12 +4,9 @@ import { supportsPassiveEvents } from 'detect-passive-events';
import React, { useEffect, useMemo, useRef, useState } from 'react'; import React, { useEffect, useMemo, useRef, useState } from 'react';
import { useHistory } from 'react-router-dom'; import { useHistory } from 'react-router-dom';
import { import { closeDropdownMenu as closeDropdownMenuRedux, openDropdownMenu } from 'soapbox/actions/dropdown-menu';
closeDropdownMenu as closeDropdownMenuRedux,
openDropdownMenu,
} from 'soapbox/actions/dropdown-menu';
import { closeModal, openModal } from 'soapbox/actions/modals'; import { closeModal, openModal } from 'soapbox/actions/modals';
import { useAppDispatch, useAppSelector } from 'soapbox/hooks'; import { useAppDispatch } from 'soapbox/hooks';
import { isUserTouching } from 'soapbox/is-mobile'; import { isUserTouching } from 'soapbox/is-mobile';
import { IconButton, Portal } from '../ui'; import { IconButton, Portal } from '../ui';
@ -53,10 +50,8 @@ const DropdownMenu = (props: IDropdownMenu) => {
const history = useHistory(); const history = useHistory();
const [isOpen, setIsOpen] = useState<boolean>(false); const [isOpen, setIsOpen] = useState<boolean>(false);
const isOpenRedux = useAppSelector(state => state.dropdown_menu.isOpen);
const arrowRef = useRef<HTMLDivElement>(null); const arrowRef = useRef<HTMLDivElement>(null);
const activeElement = useRef<Element | null>(null);
const isOnMobile = isUserTouching(); const isOnMobile = isUserTouching();
@ -116,10 +111,7 @@ const DropdownMenu = (props: IDropdownMenu) => {
}; };
const handleClose = () => { const handleClose = () => {
if (activeElement.current && activeElement.current === refs.reference.current) { (refs.reference.current as HTMLButtonElement)?.focus();
(activeElement.current as any).focus();
activeElement.current = null;
}
if (isOnMobile) { if (isOnMobile) {
dispatch(closeModal('ACTIONS')); dispatch(closeModal('ACTIONS'));
@ -134,24 +126,13 @@ const DropdownMenu = (props: IDropdownMenu) => {
}; };
const closeDropdownMenu = () => { const closeDropdownMenu = () => {
if (isOpenRedux) { dispatch((dispatch, getState) => {
dispatch(closeDropdownMenuRedux()); const isOpenRedux = getState().dropdown_menu.isOpen;
}
};
const handleMouseDown: React.EventHandler<React.MouseEvent | React.KeyboardEvent> = () => { if (isOpenRedux) {
if (!isOpen) { dispatch(closeDropdownMenuRedux());
activeElement.current = document.activeElement; }
} });
};
const handleButtonKeyDown: React.EventHandler<React.KeyboardEvent> = (event) => {
switch (event.key) {
case ' ':
case 'Enter':
handleMouseDown(event);
break;
}
}; };
const handleKeyPress: React.EventHandler<React.KeyboardEvent<HTMLButtonElement>> = (event) => { const handleKeyPress: React.EventHandler<React.KeyboardEvent<HTMLButtonElement>> = (event) => {
@ -263,16 +244,22 @@ const DropdownMenu = (props: IDropdownMenu) => {
}, []); }, []);
useEffect(() => { useEffect(() => {
document.addEventListener('click', handleDocumentClick, false); if (isOpen) {
document.addEventListener('keydown', handleKeyDown, false); if (refs.floating.current) {
document.addEventListener('touchend', handleDocumentClick, listenerOptions); (refs.floating.current?.querySelector('li a[role=\'button\']') as HTMLAnchorElement)?.focus();
}
return () => { document.addEventListener('click', handleDocumentClick, false);
document.removeEventListener('click', handleDocumentClick); document.addEventListener('keydown', handleKeyDown, false);
document.removeEventListener('keydown', handleKeyDown); document.addEventListener('touchend', handleDocumentClick, listenerOptions);
document.removeEventListener('touchend', handleDocumentClick);
}; return () => {
}, [refs.floating.current]); document.removeEventListener('click', handleDocumentClick);
document.removeEventListener('keydown', handleKeyDown);
document.removeEventListener('touchend', handleDocumentClick);
};
}
}, [isOpen, refs.floating.current]);
if (items.length === 0) { if (items.length === 0) {
return null; return null;
@ -284,8 +271,6 @@ const DropdownMenu = (props: IDropdownMenu) => {
React.cloneElement(children, { React.cloneElement(children, {
disabled, disabled,
onClick: handleClick, onClick: handleClick,
onMouseDown: handleMouseDown,
onKeyDown: handleButtonKeyDown,
onKeyPress: handleKeyPress, onKeyPress: handleKeyPress,
ref: refs.setReference, ref: refs.setReference,
}) })
@ -299,8 +284,6 @@ const DropdownMenu = (props: IDropdownMenu) => {
title={title} title={title}
src={src} src={src}
onClick={handleClick} onClick={handleClick}
onMouseDown={handleMouseDown}
onKeyDown={handleButtonKeyDown}
onKeyPress={handleKeyPress} onKeyPress={handleKeyPress}
ref={refs.setReference} ref={refs.setReference}
/> />
@ -346,4 +329,4 @@ const DropdownMenu = (props: IDropdownMenu) => {
); );
}; };
export default DropdownMenu; export default DropdownMenu;