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,
}) })
} }

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 = () => {
dispatch((dispatch, getState) => {
const isOpenRedux = getState().dropdown_menu.isOpen;
if (isOpenRedux) { if (isOpenRedux) {
dispatch(closeDropdownMenuRedux()); dispatch(closeDropdownMenuRedux());
} }
}; });
const handleMouseDown: React.EventHandler<React.MouseEvent | React.KeyboardEvent> = () => {
if (!isOpen) {
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,6 +244,11 @@ const DropdownMenu = (props: IDropdownMenu) => {
}, []); }, []);
useEffect(() => { useEffect(() => {
if (isOpen) {
if (refs.floating.current) {
(refs.floating.current?.querySelector('li a[role=\'button\']') as HTMLAnchorElement)?.focus();
}
document.addEventListener('click', handleDocumentClick, false); document.addEventListener('click', handleDocumentClick, false);
document.addEventListener('keydown', handleKeyDown, false); document.addEventListener('keydown', handleKeyDown, false);
document.addEventListener('touchend', handleDocumentClick, listenerOptions); document.addEventListener('touchend', handleDocumentClick, listenerOptions);
@ -272,7 +258,8 @@ const DropdownMenu = (props: IDropdownMenu) => {
document.removeEventListener('keydown', handleKeyDown); document.removeEventListener('keydown', handleKeyDown);
document.removeEventListener('touchend', handleDocumentClick); document.removeEventListener('touchend', handleDocumentClick);
}; };
}, [refs.floating.current]); }
}, [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}
/> />